@dichovsky/testrail-api-client 1.0.0 → 2.1.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.
Files changed (137) hide show
  1. package/README.md +22 -0
  2. package/dist/cli/auth.d.ts +21 -0
  3. package/dist/cli/auth.js +16 -0
  4. package/dist/cli/body.d.ts +42 -0
  5. package/dist/cli/body.js +89 -0
  6. package/dist/cli/dispatch.d.ts +16 -0
  7. package/dist/cli/dispatch.js +87 -0
  8. package/dist/cli/handler-context.d.ts +43 -0
  9. package/dist/cli/handler-context.js +2 -0
  10. package/dist/cli/handlers/case-write.d.ts +4 -0
  11. package/dist/cli/handlers/case-write.js +26 -0
  12. package/dist/cli/handlers/case.d.ts +4 -0
  13. package/dist/cli/handlers/case.js +11 -0
  14. package/dist/cli/handlers/milestone.d.ts +4 -0
  15. package/dist/cli/handlers/milestone.js +15 -0
  16. package/dist/cli/handlers/project.d.ts +4 -0
  17. package/dist/cli/handlers/project.js +11 -0
  18. package/dist/cli/handlers/result-write.d.ts +4 -0
  19. package/dist/cli/handlers/result-write.js +40 -0
  20. package/dist/cli/handlers/result.d.ts +3 -0
  21. package/dist/cli/handlers/result.js +11 -0
  22. package/dist/cli/handlers/run-write.d.ts +10 -0
  23. package/dist/cli/handlers/run-write.js +29 -0
  24. package/dist/cli/handlers/run.d.ts +4 -0
  25. package/dist/cli/handlers/run.js +15 -0
  26. package/dist/cli/handlers/suite.d.ts +4 -0
  27. package/dist/cli/handlers/suite.js +10 -0
  28. package/dist/cli/handlers/user.d.ts +4 -0
  29. package/dist/cli/handlers/user.js +11 -0
  30. package/dist/cli/ids.d.ts +6 -0
  31. package/dist/cli/ids.js +20 -0
  32. package/dist/cli/index.d.ts +3 -0
  33. package/dist/cli/index.js +198 -0
  34. package/dist/cli/install-skill.d.ts +35 -0
  35. package/dist/cli/install-skill.js +71 -0
  36. package/dist/cli/metadata.d.ts +37 -0
  37. package/dist/cli/metadata.js +151 -0
  38. package/dist/cli/output.d.ts +28 -0
  39. package/dist/cli/output.js +84 -0
  40. package/dist/cli.d.ts +1 -1
  41. package/dist/cli.js +1 -266
  42. package/dist/client-core.d.ts +16 -7
  43. package/dist/client-core.js +153 -27
  44. package/dist/client.d.ts +274 -118
  45. package/dist/client.js +404 -463
  46. package/dist/constants.d.ts +1 -0
  47. package/dist/constants.js +1 -0
  48. package/dist/errors.d.ts +11 -9
  49. package/dist/errors.js +12 -8
  50. package/dist/index.d.ts +4 -2
  51. package/dist/index.js +2 -1
  52. package/dist/modules/attachments.d.ts +19 -0
  53. package/dist/modules/attachments.js +64 -0
  54. package/dist/modules/cases.d.ts +13 -0
  55. package/dist/modules/cases.js +58 -0
  56. package/dist/modules/configurations.d.ts +14 -0
  57. package/dist/modules/configurations.js +37 -0
  58. package/dist/modules/datasets.d.ts +12 -0
  59. package/dist/modules/datasets.js +28 -0
  60. package/dist/modules/metadata.d.ts +14 -0
  61. package/dist/modules/metadata.js +31 -0
  62. package/dist/modules/milestones.d.ts +12 -0
  63. package/dist/modules/milestones.js +36 -0
  64. package/dist/modules/plans.d.ts +16 -0
  65. package/dist/modules/plans.js +59 -0
  66. package/dist/modules/projects.d.ts +36 -0
  67. package/dist/modules/projects.js +55 -0
  68. package/dist/modules/reports.d.ts +9 -0
  69. package/dist/modules/reports.js +16 -0
  70. package/dist/modules/results.d.ts +14 -0
  71. package/dist/modules/results.js +69 -0
  72. package/dist/modules/runs.d.ts +14 -0
  73. package/dist/modules/runs.js +57 -0
  74. package/dist/modules/sections.d.ts +16 -0
  75. package/dist/modules/sections.js +37 -0
  76. package/dist/modules/sharedSteps.d.ts +12 -0
  77. package/dist/modules/sharedSteps.js +28 -0
  78. package/dist/modules/suites.d.ts +37 -0
  79. package/dist/modules/suites.js +54 -0
  80. package/dist/modules/tests.d.ts +9 -0
  81. package/dist/modules/tests.js +25 -0
  82. package/dist/modules/users.d.ts +18 -0
  83. package/dist/modules/users.js +62 -0
  84. package/dist/modules/variables.d.ts +11 -0
  85. package/dist/modules/variables.js +24 -0
  86. package/dist/schemas.d.ts +544 -0
  87. package/dist/schemas.js +419 -0
  88. package/dist/types.d.ts +1 -55
  89. package/dist/utils.d.ts +2 -0
  90. package/dist/utils.js +4 -0
  91. package/package.json +23 -15
  92. package/skill/SKILL.md +395 -0
  93. package/src/cli/auth.ts +37 -0
  94. package/src/cli/body.ts +100 -0
  95. package/src/cli/dispatch.ts +91 -0
  96. package/src/cli/handler-context.ts +46 -0
  97. package/src/cli/handlers/case-write.ts +26 -0
  98. package/src/cli/handlers/case.ts +13 -0
  99. package/src/cli/handlers/milestone.ts +19 -0
  100. package/src/cli/handlers/project.ts +13 -0
  101. package/src/cli/handlers/result-write.ts +40 -0
  102. package/src/cli/handlers/result.ts +14 -0
  103. package/src/cli/handlers/run-write.ts +30 -0
  104. package/src/cli/handlers/run.ts +19 -0
  105. package/src/cli/handlers/suite.ts +12 -0
  106. package/src/cli/handlers/user.ts +13 -0
  107. package/src/cli/ids.ts +20 -0
  108. package/src/cli/index.ts +224 -0
  109. package/src/cli/install-skill.ts +89 -0
  110. package/src/cli/metadata.ts +194 -0
  111. package/src/cli/output.ts +96 -0
  112. package/src/cli.ts +1 -286
  113. package/src/client-core.ts +183 -67
  114. package/src/client.ts +414 -483
  115. package/src/constants.ts +1 -0
  116. package/src/errors.ts +18 -11
  117. package/src/index.ts +50 -8
  118. package/src/modules/attachments.ts +125 -0
  119. package/src/modules/cases.ts +78 -0
  120. package/src/modules/configurations.ts +68 -0
  121. package/src/modules/datasets.ts +44 -0
  122. package/src/modules/metadata.ts +63 -0
  123. package/src/modules/milestones.ts +54 -0
  124. package/src/modules/plans.ts +89 -0
  125. package/src/modules/projects.ts +67 -0
  126. package/src/modules/reports.ts +23 -0
  127. package/src/modules/results.ts +90 -0
  128. package/src/modules/runs.ts +70 -0
  129. package/src/modules/sections.ts +55 -0
  130. package/src/modules/sharedSteps.ts +44 -0
  131. package/src/modules/suites.ts +67 -0
  132. package/src/modules/tests.ts +28 -0
  133. package/src/modules/users.ts +87 -0
  134. package/src/modules/variables.ts +36 -0
  135. package/src/schemas.ts +551 -0
  136. package/src/types.ts +11 -60
  137. package/src/utils.ts +5 -0
package/src/client.ts CHANGED
@@ -11,8 +11,6 @@ import type {
11
11
  User,
12
12
  Status,
13
13
  Priority,
14
- AddCasePayload,
15
- UpdateCasePayload,
16
14
  AddSuitePayload,
17
15
  UpdateSuitePayload,
18
16
  AddPlanPayload,
@@ -20,10 +18,6 @@ import type {
20
18
  AddPlanEntryPayload,
21
19
  UpdatePlanEntryPayload,
22
20
  PlanEntry,
23
- AddRunPayload,
24
- UpdateRunPayload,
25
- AddResultPayload,
26
- AddResultsForCasesPayload,
27
21
  AddSectionPayload,
28
22
  UpdateSectionPayload,
29
23
  AddMilestonePayload,
@@ -65,8 +59,32 @@ import type {
65
59
  Report,
66
60
  ReportResult,
67
61
  } from './types.js';
62
+ import type {
63
+ AddCasePayload,
64
+ UpdateCasePayload,
65
+ AddRunPayload,
66
+ UpdateRunPayload,
67
+ AddResultPayload,
68
+ AddResultsForCasesPayload,
69
+ } from './schemas.js';
68
70
  import { TestRailClientCore } from './client-core.js';
69
- import { TestRailValidationError } from './errors.js';
71
+ import { ProjectModule } from './modules/projects.js';
72
+ import { SuiteModule } from './modules/suites.js';
73
+ import { SectionModule } from './modules/sections.js';
74
+ import { CaseModule } from './modules/cases.js';
75
+ import { PlanModule } from './modules/plans.js';
76
+ import { RunModule } from './modules/runs.js';
77
+ import { TestModule } from './modules/tests.js';
78
+ import { ResultModule } from './modules/results.js';
79
+ import { MilestoneModule } from './modules/milestones.js';
80
+ import { UsersModule } from './modules/users.js';
81
+ import { MetadataModule } from './modules/metadata.js';
82
+ import { ConfigurationModule } from './modules/configurations.js';
83
+ import { AttachmentModule } from './modules/attachments.js';
84
+ import { SharedStepModule } from './modules/sharedSteps.js';
85
+ import { VariableModule } from './modules/variables.js';
86
+ import { DatasetModule } from './modules/datasets.js';
87
+ import { ReportModule } from './modules/reports.js';
70
88
 
71
89
  export { TestRailApiError, TestRailValidationError } from './errors.js';
72
90
 
@@ -78,6 +96,46 @@ export { TestRailApiError, TestRailValidationError } from './errors.js';
78
96
  * Extends {@link TestRailClientCore} for HTTP pipeline, caching, rate limiting, and retry.
79
97
  */
80
98
  export class TestRailClient extends TestRailClientCore {
99
+ // ── Domain modules ────────────────────────────────────────────────────────
100
+ public readonly projects: ProjectModule;
101
+ public readonly suites: SuiteModule;
102
+ public readonly sections: SectionModule;
103
+ public readonly cases: CaseModule;
104
+ public readonly plans: PlanModule;
105
+ public readonly runs: RunModule;
106
+ public readonly tests: TestModule;
107
+ public readonly results: ResultModule;
108
+ public readonly milestones: MilestoneModule;
109
+ public readonly users: UsersModule;
110
+ public readonly metadata: MetadataModule;
111
+ public readonly configurations: ConfigurationModule;
112
+ public readonly attachments: AttachmentModule;
113
+ public readonly sharedSteps: SharedStepModule;
114
+ public readonly variables: VariableModule;
115
+ public readonly datasets: DatasetModule;
116
+ public readonly reports: ReportModule;
117
+
118
+ constructor(...args: ConstructorParameters<typeof TestRailClientCore>) {
119
+ super(...args);
120
+ this.projects = new ProjectModule(this);
121
+ this.suites = new SuiteModule(this);
122
+ this.sections = new SectionModule(this);
123
+ this.cases = new CaseModule(this);
124
+ this.plans = new PlanModule(this);
125
+ this.runs = new RunModule(this);
126
+ this.tests = new TestModule(this);
127
+ this.results = new ResultModule(this);
128
+ this.milestones = new MilestoneModule(this);
129
+ this.users = new UsersModule(this);
130
+ this.metadata = new MetadataModule(this);
131
+ this.configurations = new ConfigurationModule(this);
132
+ this.attachments = new AttachmentModule(this);
133
+ this.sharedSteps = new SharedStepModule(this);
134
+ this.variables = new VariableModule(this);
135
+ this.datasets = new DatasetModule(this);
136
+ this.reports = new ReportModule(this);
137
+ }
138
+
81
139
  // ── Projects ──────────────────────────────────────────────────────────────
82
140
 
83
141
  /**
@@ -86,8 +144,7 @@ export class TestRailClient extends TestRailClientCore {
86
144
  * @throws {TestRailApiError} When the API request fails
87
145
  */
88
146
  async getProject(projectId: number): Promise<Project> {
89
- this.validateId(projectId, 'projectId');
90
- return this.request<Project>('GET', `get_project/${projectId}`);
147
+ return this.projects.getProject(projectId);
91
148
  }
92
149
 
93
150
  /**
@@ -96,10 +153,7 @@ export class TestRailClient extends TestRailClientCore {
96
153
  * @throws {TestRailApiError} When the API request fails
97
154
  */
98
155
  async getProjects(limit?: number, offset?: number): Promise<Project[]> {
99
- this.validatePaginationParams(limit, offset);
100
- const endpoint = this.buildEndpoint('get_projects', { limit, offset });
101
- const response = await this.request<{ projects: Project[] }>('GET', endpoint);
102
- return response.projects ?? [];
156
+ return this.projects.getProjects(limit, offset);
103
157
  }
104
158
 
105
159
  /**
@@ -107,7 +161,7 @@ export class TestRailClient extends TestRailClientCore {
107
161
  * @throws {TestRailApiError} When the API request fails
108
162
  */
109
163
  async addProject(payload: AddProjectPayload): Promise<Project> {
110
- return this.request<Project>('POST', 'add_project', payload);
164
+ return this.projects.addProject(payload);
111
165
  }
112
166
 
113
167
  /**
@@ -116,8 +170,7 @@ export class TestRailClient extends TestRailClientCore {
116
170
  * @throws {TestRailApiError} When the API request fails
117
171
  */
118
172
  async updateProject(projectId: number, payload: UpdateProjectPayload): Promise<Project> {
119
- this.validateId(projectId, 'projectId');
120
- return this.request<Project>('POST', `update_project/${projectId}`, payload);
173
+ return this.projects.updateProject(projectId, payload);
121
174
  }
122
175
 
123
176
  /**
@@ -126,8 +179,7 @@ export class TestRailClient extends TestRailClientCore {
126
179
  * @throws {TestRailApiError} When the API request fails
127
180
  */
128
181
  async deleteProject(projectId: number): Promise<void> {
129
- this.validateId(projectId, 'projectId');
130
- await this.request<void>('POST', `delete_project/${projectId}`);
182
+ return this.projects.deleteProject(projectId);
131
183
  }
132
184
 
133
185
  // ── Suites ────────────────────────────────────────────────────────────────
@@ -138,8 +190,7 @@ export class TestRailClient extends TestRailClientCore {
138
190
  * @throws {TestRailApiError} When the API request fails
139
191
  */
140
192
  async getSuite(suiteId: number): Promise<Suite> {
141
- this.validateId(suiteId, 'suiteId');
142
- return this.request<Suite>('GET', `get_suite/${suiteId}`);
193
+ return this.suites.getSuite(suiteId);
143
194
  }
144
195
 
145
196
  /**
@@ -148,8 +199,7 @@ export class TestRailClient extends TestRailClientCore {
148
199
  * @throws {TestRailApiError} When the API request fails
149
200
  */
150
201
  async getSuites(projectId: number): Promise<Suite[]> {
151
- this.validateId(projectId, 'projectId');
152
- return this.request<Suite[]>('GET', `get_suites/${projectId}`);
202
+ return this.suites.getSuites(projectId);
153
203
  }
154
204
 
155
205
  /**
@@ -158,8 +208,7 @@ export class TestRailClient extends TestRailClientCore {
158
208
  * @throws {TestRailApiError} When the API request fails
159
209
  */
160
210
  async addSuite(projectId: number, payload: AddSuitePayload): Promise<Suite> {
161
- this.validateId(projectId, 'projectId');
162
- return this.request<Suite>('POST', `add_suite/${projectId}`, payload);
211
+ return this.suites.addSuite(projectId, payload);
163
212
  }
164
213
 
165
214
  /**
@@ -168,8 +217,7 @@ export class TestRailClient extends TestRailClientCore {
168
217
  * @throws {TestRailApiError} When the API request fails
169
218
  */
170
219
  async updateSuite(suiteId: number, payload: UpdateSuitePayload): Promise<Suite> {
171
- this.validateId(suiteId, 'suiteId');
172
- return this.request<Suite>('POST', `update_suite/${suiteId}`, payload);
220
+ return this.suites.updateSuite(suiteId, payload);
173
221
  }
174
222
 
175
223
  /**
@@ -178,8 +226,7 @@ export class TestRailClient extends TestRailClientCore {
178
226
  * @throws {TestRailApiError} When the API request fails
179
227
  */
180
228
  async deleteSuite(suiteId: number): Promise<void> {
181
- this.validateId(suiteId, 'suiteId');
182
- await this.request<void>('POST', `delete_suite/${suiteId}`);
229
+ return this.suites.deleteSuite(suiteId);
183
230
  }
184
231
 
185
232
  // ── Sections ──────────────────────────────────────────────────────────────
@@ -190,8 +237,7 @@ export class TestRailClient extends TestRailClientCore {
190
237
  * @throws {TestRailApiError} When the API request fails
191
238
  */
192
239
  async getSection(sectionId: number): Promise<Section> {
193
- this.validateId(sectionId, 'sectionId');
194
- return this.request<Section>('GET', `get_section/${sectionId}`);
240
+ return this.sections.getSection(sectionId);
195
241
  }
196
242
 
197
243
  /**
@@ -206,15 +252,7 @@ export class TestRailClient extends TestRailClientCore {
206
252
  projectId: number,
207
253
  options?: { suiteId?: number; limit?: number; offset?: number },
208
254
  ): Promise<Section[]> {
209
- this.validateId(projectId, 'projectId');
210
- const { suiteId, limit, offset } = options ?? {};
211
- if (suiteId !== undefined) {
212
- this.validateId(suiteId, 'suiteId');
213
- }
214
- this.validatePaginationParams(limit, offset);
215
- const endpoint = this.buildEndpoint(`get_sections/${projectId}`, { suite_id: suiteId, limit, offset });
216
- const response = await this.request<{ sections: Section[] }>('GET', endpoint);
217
- return response.sections ?? [];
255
+ return this.sections.getSections(projectId, options);
218
256
  }
219
257
 
220
258
  /**
@@ -223,8 +261,7 @@ export class TestRailClient extends TestRailClientCore {
223
261
  * @throws {TestRailApiError} When the API request fails
224
262
  */
225
263
  async addSection(projectId: number, payload: AddSectionPayload): Promise<Section> {
226
- this.validateId(projectId, 'projectId');
227
- return this.request<Section>('POST', `add_section/${projectId}`, payload);
264
+ return this.sections.addSection(projectId, payload);
228
265
  }
229
266
 
230
267
  /**
@@ -233,8 +270,7 @@ export class TestRailClient extends TestRailClientCore {
233
270
  * @throws {TestRailApiError} When the API request fails
234
271
  */
235
272
  async updateSection(sectionId: number, payload: UpdateSectionPayload): Promise<Section> {
236
- this.validateId(sectionId, 'sectionId');
237
- return this.request<Section>('POST', `update_section/${sectionId}`, payload);
273
+ return this.sections.updateSection(sectionId, payload);
238
274
  }
239
275
 
240
276
  /**
@@ -243,8 +279,7 @@ export class TestRailClient extends TestRailClientCore {
243
279
  * @throws {TestRailApiError} When the API request fails
244
280
  */
245
281
  async deleteSection(sectionId: number): Promise<void> {
246
- this.validateId(sectionId, 'sectionId');
247
- await this.request<void>('POST', `delete_section/${sectionId}`);
282
+ return this.sections.deleteSection(sectionId);
248
283
  }
249
284
 
250
285
  // ── Cases ─────────────────────────────────────────────────────────────────
@@ -255,8 +290,7 @@ export class TestRailClient extends TestRailClientCore {
255
290
  * @throws {TestRailApiError} When the API request fails
256
291
  */
257
292
  async getCase(caseId: number): Promise<Case> {
258
- this.validateId(caseId, 'caseId');
259
- return this.request<Case>('GET', `get_case/${caseId}`);
293
+ return this.cases.getCase(caseId);
260
294
  }
261
295
 
262
296
  /**
@@ -277,44 +311,7 @@ export class TestRailClient extends TestRailClientCore {
277
311
  * @throws {TestRailApiError} When the API request fails
278
312
  */
279
313
  async getCases(projectId: number, options?: GetCasesOptions): Promise<Case[]> {
280
- this.validateId(projectId, 'projectId');
281
- const {
282
- suiteId,
283
- sectionId,
284
- typeId,
285
- priorityId,
286
- templateId,
287
- milestoneId,
288
- createdAfter,
289
- createdBefore,
290
- updatedAfter,
291
- updatedBefore,
292
- limit,
293
- offset,
294
- } = options ?? {};
295
- if (suiteId !== undefined) this.validateId(suiteId, 'suiteId');
296
- if (sectionId !== undefined) this.validateId(sectionId, 'sectionId');
297
- if (typeId !== undefined) this.validateId(typeId, 'typeId');
298
- if (priorityId !== undefined) this.validateId(priorityId, 'priorityId');
299
- if (templateId !== undefined) this.validateId(templateId, 'templateId');
300
- if (milestoneId !== undefined) this.validateId(milestoneId, 'milestoneId');
301
- this.validatePaginationParams(limit, offset);
302
- const endpoint = this.buildEndpoint(`get_cases/${projectId}`, {
303
- suite_id: suiteId,
304
- section_id: sectionId,
305
- type_id: typeId,
306
- priority_id: priorityId,
307
- template_id: templateId,
308
- milestone_id: milestoneId,
309
- created_after: createdAfter,
310
- created_before: createdBefore,
311
- updated_after: updatedAfter,
312
- updated_before: updatedBefore,
313
- limit,
314
- offset,
315
- });
316
- const response = await this.request<{ cases: Case[] }>('GET', endpoint);
317
- return response.cases ?? [];
314
+ return this.cases.getCases(projectId, options);
318
315
  }
319
316
 
320
317
  /**
@@ -323,8 +320,7 @@ export class TestRailClient extends TestRailClientCore {
323
320
  * @throws {TestRailApiError} When the API request fails
324
321
  */
325
322
  async addCase(sectionId: number, payload: AddCasePayload): Promise<Case> {
326
- this.validateId(sectionId, 'sectionId');
327
- return this.request<Case>('POST', `add_case/${sectionId}`, payload);
323
+ return this.cases.addCase(sectionId, payload);
328
324
  }
329
325
 
330
326
  /**
@@ -333,8 +329,7 @@ export class TestRailClient extends TestRailClientCore {
333
329
  * @throws {TestRailApiError} When the API request fails
334
330
  */
335
331
  async updateCase(caseId: number, payload: UpdateCasePayload): Promise<Case> {
336
- this.validateId(caseId, 'caseId');
337
- return this.request<Case>('POST', `update_case/${caseId}`, payload);
332
+ return this.cases.updateCase(caseId, payload);
338
333
  }
339
334
 
340
335
  /**
@@ -343,8 +338,7 @@ export class TestRailClient extends TestRailClientCore {
343
338
  * @throws {TestRailApiError} When the API request fails
344
339
  */
345
340
  async deleteCase(caseId: number): Promise<void> {
346
- this.validateId(caseId, 'caseId');
347
- await this.request<void>('POST', `delete_case/${caseId}`);
341
+ return this.cases.deleteCase(caseId);
348
342
  }
349
343
 
350
344
  // ── Plans ─────────────────────────────────────────────────────────────────
@@ -355,8 +349,7 @@ export class TestRailClient extends TestRailClientCore {
355
349
  * @throws {TestRailApiError} When the API request fails
356
350
  */
357
351
  async getPlan(planId: number): Promise<Plan> {
358
- this.validateId(planId, 'planId');
359
- return this.request<Plan>('GET', `get_plan/${planId}`);
352
+ return this.plans.getPlan(planId);
360
353
  }
361
354
 
362
355
  /**
@@ -368,19 +361,7 @@ export class TestRailClient extends TestRailClientCore {
368
361
  * @throws {TestRailApiError} When the API request fails
369
362
  */
370
363
  async getPlans(projectId: number, options?: GetPlansOptions): Promise<Plan[]> {
371
- this.validateId(projectId, 'projectId');
372
- this.validatePaginationParams(options?.limit, options?.offset);
373
- const endpoint = this.buildEndpoint(`get_plans/${projectId}`, {
374
- created_after: options?.created_after,
375
- created_before: options?.created_before,
376
- created_by: this.serializeIdList(options?.created_by),
377
- is_completed: options?.is_completed,
378
- milestone_id: this.serializeIdList(options?.milestone_id),
379
- limit: options?.limit,
380
- offset: options?.offset,
381
- });
382
- const response = await this.request<{ plans: Plan[] }>('GET', endpoint);
383
- return response.plans ?? [];
364
+ return this.plans.getPlans(projectId, options);
384
365
  }
385
366
 
386
367
  /**
@@ -389,8 +370,7 @@ export class TestRailClient extends TestRailClientCore {
389
370
  * @throws {TestRailApiError} When the API request fails
390
371
  */
391
372
  async addPlan(projectId: number, payload: AddPlanPayload): Promise<Plan> {
392
- this.validateId(projectId, 'projectId');
393
- return this.request<Plan>('POST', `add_plan/${projectId}`, payload);
373
+ return this.plans.addPlan(projectId, payload);
394
374
  }
395
375
 
396
376
  /**
@@ -399,8 +379,7 @@ export class TestRailClient extends TestRailClientCore {
399
379
  * @throws {TestRailApiError} When the API request fails
400
380
  */
401
381
  async updatePlan(planId: number, payload: UpdatePlanPayload): Promise<Plan> {
402
- this.validateId(planId, 'planId');
403
- return this.request<Plan>('POST', `update_plan/${planId}`, payload);
382
+ return this.plans.updatePlan(planId, payload);
404
383
  }
405
384
 
406
385
  /**
@@ -409,8 +388,7 @@ export class TestRailClient extends TestRailClientCore {
409
388
  * @throws {TestRailApiError} When the API request fails
410
389
  */
411
390
  async closePlan(planId: number): Promise<Plan> {
412
- this.validateId(planId, 'planId');
413
- return this.request<Plan>('POST', `close_plan/${planId}`);
391
+ return this.plans.closePlan(planId);
414
392
  }
415
393
 
416
394
  /**
@@ -419,8 +397,7 @@ export class TestRailClient extends TestRailClientCore {
419
397
  * @throws {TestRailApiError} When the API request fails
420
398
  */
421
399
  async deletePlan(planId: number): Promise<void> {
422
- this.validateId(planId, 'planId');
423
- await this.request<void>('POST', `delete_plan/${planId}`);
400
+ return this.plans.deletePlan(planId);
424
401
  }
425
402
 
426
403
  /**
@@ -429,8 +406,7 @@ export class TestRailClient extends TestRailClientCore {
429
406
  * @throws {TestRailApiError} When the API request fails
430
407
  */
431
408
  async addPlanEntry(planId: number, payload: AddPlanEntryPayload): Promise<PlanEntry> {
432
- this.validateId(planId, 'planId');
433
- return this.request<PlanEntry>('POST', `add_plan_entry/${planId}`, payload);
409
+ return this.plans.addPlanEntry(planId, payload);
434
410
  }
435
411
 
436
412
  /**
@@ -439,9 +415,7 @@ export class TestRailClient extends TestRailClientCore {
439
415
  * @throws {TestRailApiError} When the API request fails
440
416
  */
441
417
  async updatePlanEntry(planId: number, entryId: string, payload: UpdatePlanEntryPayload): Promise<PlanEntry> {
442
- this.validateId(planId, 'planId');
443
- this.validateEntryId(entryId);
444
- return this.request<PlanEntry>('POST', `update_plan_entry/${planId}/${entryId}`, payload);
418
+ return this.plans.updatePlanEntry(planId, entryId, payload);
445
419
  }
446
420
 
447
421
  /**
@@ -450,9 +424,7 @@ export class TestRailClient extends TestRailClientCore {
450
424
  * @throws {TestRailApiError} When the API request fails
451
425
  */
452
426
  async deletePlanEntry(planId: number, entryId: string): Promise<void> {
453
- this.validateId(planId, 'planId');
454
- this.validateEntryId(entryId);
455
- await this.request<void>('POST', `delete_plan_entry/${planId}/${entryId}`);
427
+ return this.plans.deletePlanEntry(planId, entryId);
456
428
  }
457
429
 
458
430
  // ── Runs ──────────────────────────────────────────────────────────────────
@@ -463,8 +435,7 @@ export class TestRailClient extends TestRailClientCore {
463
435
  * @throws {TestRailApiError} When the API request fails
464
436
  */
465
437
  async getRun(runId: number): Promise<Run> {
466
- this.validateId(runId, 'runId');
467
- return this.request<Run>('GET', `get_run/${runId}`);
438
+ return this.runs.getRun(runId);
468
439
  }
469
440
 
470
441
  /**
@@ -476,33 +447,7 @@ export class TestRailClient extends TestRailClientCore {
476
447
  * @throws {TestRailApiError} When the API request fails
477
448
  */
478
449
  async getRuns(projectId: number, options?: GetRunsOptions): Promise<Run[]> {
479
- this.validateId(projectId, 'projectId');
480
- const { createdAfter, createdBefore, createdBy, isCompleted, milestoneId, refsFilter, suiteId, limit, offset } =
481
- options ?? {};
482
- this.validatePaginationParams(limit, offset);
483
- if (milestoneId !== undefined) {
484
- this.validateId(milestoneId, 'milestoneId');
485
- }
486
- if (suiteId !== undefined) {
487
- this.validateId(suiteId, 'suiteId');
488
- }
489
- if (createdBy !== undefined) {
490
- createdBy.forEach((userId) => this.validateId(userId, 'createdBy'));
491
- }
492
- const createdByFilter = createdBy && createdBy.length > 0 ? createdBy.join(',') : undefined;
493
- const endpoint = this.buildEndpoint(`get_runs/${projectId}`, {
494
- created_after: createdAfter,
495
- created_before: createdBefore,
496
- created_by: createdByFilter,
497
- is_completed: isCompleted !== undefined ? (isCompleted ? 1 : 0) : undefined,
498
- milestone_id: milestoneId,
499
- refs_filter: refsFilter,
500
- suite_id: suiteId,
501
- limit,
502
- offset,
503
- });
504
- const response = await this.request<{ runs: Run[] }>('GET', endpoint);
505
- return response.runs ?? [];
450
+ return this.runs.getRuns(projectId, options);
506
451
  }
507
452
 
508
453
  /**
@@ -511,8 +456,7 @@ export class TestRailClient extends TestRailClientCore {
511
456
  * @throws {TestRailApiError} When the API request fails
512
457
  */
513
458
  async addRun(projectId: number, payload: AddRunPayload): Promise<Run> {
514
- this.validateId(projectId, 'projectId');
515
- return this.request<Run>('POST', `add_run/${projectId}`, payload);
459
+ return this.runs.addRun(projectId, payload);
516
460
  }
517
461
 
518
462
  /**
@@ -521,8 +465,7 @@ export class TestRailClient extends TestRailClientCore {
521
465
  * @throws {TestRailApiError} When the API request fails
522
466
  */
523
467
  async updateRun(runId: number, payload: UpdateRunPayload): Promise<Run> {
524
- this.validateId(runId, 'runId');
525
- return this.request<Run>('POST', `update_run/${runId}`, payload);
468
+ return this.runs.updateRun(runId, payload);
526
469
  }
527
470
 
528
471
  /**
@@ -531,8 +474,7 @@ export class TestRailClient extends TestRailClientCore {
531
474
  * @throws {TestRailApiError} When the API request fails
532
475
  */
533
476
  async closeRun(runId: number): Promise<Run> {
534
- this.validateId(runId, 'runId');
535
- return this.request<Run>('POST', `close_run/${runId}`);
477
+ return this.runs.closeRun(runId);
536
478
  }
537
479
 
538
480
  /**
@@ -541,8 +483,7 @@ export class TestRailClient extends TestRailClientCore {
541
483
  * @throws {TestRailApiError} When the API request fails
542
484
  */
543
485
  async deleteRun(runId: number): Promise<void> {
544
- this.validateId(runId, 'runId');
545
- await this.request<void>('POST', `delete_run/${runId}`);
486
+ return this.runs.deleteRun(runId);
546
487
  }
547
488
 
548
489
  // ── Tests ─────────────────────────────────────────────────────────────────
@@ -553,8 +494,7 @@ export class TestRailClient extends TestRailClientCore {
553
494
  * @throws {TestRailApiError} When the API request fails
554
495
  */
555
496
  async getTest(testId: number): Promise<Test> {
556
- this.validateId(testId, 'testId');
557
- return this.request<Test>('GET', `get_test/${testId}`);
497
+ return this.tests.getTest(testId);
558
498
  }
559
499
 
560
500
  /**
@@ -565,15 +505,7 @@ export class TestRailClient extends TestRailClientCore {
565
505
  * @throws {TestRailApiError} When the API request fails
566
506
  */
567
507
  async getTests(runId: number, options?: GetTestsOptions): Promise<Test[]> {
568
- this.validateId(runId, 'runId');
569
- this.validatePaginationParams(options?.limit, options?.offset);
570
- const endpoint = this.buildEndpoint(`get_tests/${runId}`, {
571
- status_id: this.serializeIdList(options?.status_id),
572
- limit: options?.limit,
573
- offset: options?.offset,
574
- });
575
- const response = await this.request<{ tests: Test[] }>('GET', endpoint);
576
- return response.tests ?? [];
508
+ return this.tests.getTests(runId, options);
577
509
  }
578
510
 
579
511
  // ── Results ───────────────────────────────────────────────────────────────
@@ -587,18 +519,7 @@ export class TestRailClient extends TestRailClientCore {
587
519
  * @throws {TestRailApiError} When the API request fails
588
520
  */
589
521
  async getResults(testId: number, options?: GetResultsOptions): Promise<Result[]> {
590
- this.validateId(testId, 'testId');
591
- this.validatePaginationParams(options?.limit, options?.offset);
592
- const endpoint = this.buildEndpoint(`get_results/${testId}`, {
593
- created_after: options?.created_after,
594
- created_before: options?.created_before,
595
- created_by: this.serializeIdList(options?.created_by),
596
- status_id: this.serializeIdList(options?.status_id),
597
- limit: options?.limit,
598
- offset: options?.offset,
599
- });
600
- const response = await this.request<{ results: Result[] }>('GET', endpoint);
601
- return response.results ?? [];
522
+ return this.results.getResults(testId, options);
602
523
  }
603
524
 
604
525
  /**
@@ -611,19 +532,7 @@ export class TestRailClient extends TestRailClientCore {
611
532
  * @throws {TestRailApiError} When the API request fails
612
533
  */
613
534
  async getResultsForCase(runId: number, caseId: number, options?: GetResultsOptions): Promise<Result[]> {
614
- this.validateId(runId, 'runId');
615
- this.validateId(caseId, 'caseId');
616
- this.validatePaginationParams(options?.limit, options?.offset);
617
- const endpoint = this.buildEndpoint(`get_results_for_case/${runId}/${caseId}`, {
618
- created_after: options?.created_after,
619
- created_before: options?.created_before,
620
- created_by: this.serializeIdList(options?.created_by),
621
- status_id: this.serializeIdList(options?.status_id),
622
- limit: options?.limit,
623
- offset: options?.offset,
624
- });
625
- const response = await this.request<{ results: Result[] }>('GET', endpoint);
626
- return response.results ?? [];
535
+ return this.results.getResultsForCase(runId, caseId, options);
627
536
  }
628
537
 
629
538
  /**
@@ -635,18 +544,7 @@ export class TestRailClient extends TestRailClientCore {
635
544
  * @throws {TestRailApiError} When the API request fails
636
545
  */
637
546
  async getResultsForRun(runId: number, options?: GetResultsOptions): Promise<Result[]> {
638
- this.validateId(runId, 'runId');
639
- this.validatePaginationParams(options?.limit, options?.offset);
640
- const endpoint = this.buildEndpoint(`get_results_for_run/${runId}`, {
641
- created_after: options?.created_after,
642
- created_before: options?.created_before,
643
- created_by: this.serializeIdList(options?.created_by),
644
- status_id: this.serializeIdList(options?.status_id),
645
- limit: options?.limit,
646
- offset: options?.offset,
647
- });
648
- const response = await this.request<{ results: Result[] }>('GET', endpoint);
649
- return response.results ?? [];
547
+ return this.results.getResultsForRun(runId, options);
650
548
  }
651
549
 
652
550
  /**
@@ -655,8 +553,7 @@ export class TestRailClient extends TestRailClientCore {
655
553
  * @throws {TestRailApiError} When the API request fails
656
554
  */
657
555
  async addResult(testId: number, payload: AddResultPayload): Promise<Result> {
658
- this.validateId(testId, 'testId');
659
- return this.request<Result>('POST', `add_result/${testId}`, payload);
556
+ return this.results.addResult(testId, payload);
660
557
  }
661
558
 
662
559
  /**
@@ -665,9 +562,7 @@ export class TestRailClient extends TestRailClientCore {
665
562
  * @throws {TestRailApiError} When the API request fails
666
563
  */
667
564
  async addResultForCase(runId: number, caseId: number, payload: AddResultPayload): Promise<Result> {
668
- this.validateId(runId, 'runId');
669
- this.validateId(caseId, 'caseId');
670
- return this.request<Result>('POST', `add_result_for_case/${runId}/${caseId}`, payload);
565
+ return this.results.addResultForCase(runId, caseId, payload);
671
566
  }
672
567
 
673
568
  /**
@@ -676,8 +571,7 @@ export class TestRailClient extends TestRailClientCore {
676
571
  * @throws {TestRailApiError} When the API request fails
677
572
  */
678
573
  async addResultsForCases(runId: number, payload: AddResultsForCasesPayload): Promise<Result[]> {
679
- this.validateId(runId, 'runId');
680
- return this.request<Result[]>('POST', `add_results_for_cases/${runId}`, payload);
574
+ return this.results.addResultsForCases(runId, payload);
681
575
  }
682
576
 
683
577
  // ── Milestones ────────────────────────────────────────────────────────────
@@ -688,105 +582,91 @@ export class TestRailClient extends TestRailClientCore {
688
582
  * @throws {TestRailApiError} When the API request fails
689
583
  */
690
584
  async getMilestone(milestoneId: number): Promise<Milestone> {
691
- this.validateId(milestoneId, 'milestoneId');
692
- return this.request<Milestone>('GET', `get_milestone/${milestoneId}`);
585
+ return this.milestones.getMilestone(milestoneId);
693
586
  }
694
587
 
695
588
  /**
696
- * Get all milestones for a project with optional filters.
697
- * @param projectId - The project ID
698
- * @param options - Optional filter parameters (is_completed, limit, offset)
699
- * @throws {TestRailValidationError} When projectId is invalid
589
+ * Get milestones.
590
+ *
591
+ * @param projectId - Project identifier.
592
+ * @param options - Optional filters and pagination settings.
593
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
700
594
  * @throws {TestRailApiError} When the API request fails
701
595
  */
702
596
  async getMilestones(projectId: number, options?: GetMilestonesOptions): Promise<Milestone[]> {
703
- this.validateId(projectId, 'projectId');
704
- this.validatePaginationParams(options?.limit, options?.offset);
705
- const endpoint = this.buildEndpoint(`get_milestones/${projectId}`, {
706
- is_completed: options?.is_completed,
707
- limit: options?.limit,
708
- offset: options?.offset,
709
- });
710
- const response = await this.request<{ milestones: Milestone[] }>('GET', endpoint);
711
- return response.milestones ?? [];
597
+ return this.milestones.getMilestones(projectId, options);
712
598
  }
713
599
 
714
600
  /**
715
- * Add a new milestone to a project.
716
- * @throws {TestRailValidationError} When projectId is invalid
601
+ * Add milestone.
602
+ *
603
+ * @param projectId - Project identifier.
604
+ * @param payload - Request payload for this operation.
605
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
717
606
  * @throws {TestRailApiError} When the API request fails
718
607
  */
719
608
  async addMilestone(projectId: number, payload: AddMilestonePayload): Promise<Milestone> {
720
- this.validateId(projectId, 'projectId');
721
- return this.request<Milestone>('POST', `add_milestone/${projectId}`, payload);
609
+ return this.milestones.addMilestone(projectId, payload);
722
610
  }
723
611
 
724
612
  /**
725
- * Update an existing milestone.
726
- * @throws {TestRailValidationError} When milestoneId is invalid
613
+ * Update milestone.
614
+ *
615
+ * @param milestoneId - Milestone identifier.
616
+ * @param payload - Request payload for this operation.
617
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
727
618
  * @throws {TestRailApiError} When the API request fails
728
619
  */
729
620
  async updateMilestone(milestoneId: number, payload: UpdateMilestonePayload): Promise<Milestone> {
730
- this.validateId(milestoneId, 'milestoneId');
731
- return this.request<Milestone>('POST', `update_milestone/${milestoneId}`, payload);
621
+ return this.milestones.updateMilestone(milestoneId, payload);
732
622
  }
733
623
 
734
624
  /**
735
- * Delete a milestone.
736
- * @throws {TestRailValidationError} When milestoneId is invalid
625
+ * Delete milestone.
626
+ *
627
+ * @param milestoneId - Milestone identifier.
628
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
737
629
  * @throws {TestRailApiError} When the API request fails
738
630
  */
739
631
  async deleteMilestone(milestoneId: number): Promise<void> {
740
- this.validateId(milestoneId, 'milestoneId');
741
- await this.request<void>('POST', `delete_milestone/${milestoneId}`);
632
+ return this.milestones.deleteMilestone(milestoneId);
742
633
  }
743
634
 
744
635
  // ── Users ─────────────────────────────────────────────────────────────────
745
636
 
746
637
  /**
747
- * Get a user by ID.
748
- * @throws {TestRailValidationError} When userId is invalid
638
+ * Get user.
639
+ *
640
+ * @param userId - User identifier.
641
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
749
642
  * @throws {TestRailApiError} When the API request fails
750
643
  */
751
644
  async getUser(userId: number): Promise<User> {
752
- this.validateId(userId, 'userId');
753
- return this.request<User>('GET', `get_user/${userId}`);
645
+ return this.users.getUser(userId);
754
646
  }
755
647
 
756
648
  /**
757
- * Get a user by email address.
649
+ * Get user by email.
650
+ *
651
+ * @param email - Email address to look up.
758
652
  * @throws {TestRailValidationError} When email format is invalid
759
653
  * @throws {TestRailApiError} When the API request fails
760
654
  */
761
655
  async getUserByEmail(email: string): Promise<User> {
762
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
763
- if (!emailRegex.test(email)) {
764
- throw new TestRailValidationError('Invalid email format');
765
- }
766
-
767
- // buildEndpoint now encodes all values via encodeURIComponent internally.
768
- return this.request<User>('GET', this.buildEndpoint('get_user_by_email', { email }));
656
+ return this.users.getUserByEmail(email);
769
657
  }
770
658
 
771
659
  /**
772
- * Get all users, optionally scoped to a project.
773
- * @param limit - Maximum number of users to return
774
- * @param offset - Number of users to skip
775
- * @param projectId - When provided, returns only users with access to the specified project
776
- * @throws {TestRailValidationError} When pagination or projectId is invalid
660
+ * Get users.
661
+ *
662
+ * @param limit - Optional maximum number of results to return.
663
+ * @param offset - Optional number of results to skip before returning results.
664
+ * @param projectId - Optional project identifier to scope results when provided.
665
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
777
666
  * @throws {TestRailApiError} When the API request fails
778
667
  */
779
668
  async getUsers(limit?: number, offset?: number, projectId?: number): Promise<User[]> {
780
- this.validatePaginationParams(limit, offset);
781
- if (projectId !== undefined) {
782
- this.validateId(projectId, 'projectId');
783
- }
784
- const endpoint = this.buildEndpoint(projectId !== undefined ? `get_users/${projectId}` : 'get_users', {
785
- limit,
786
- offset,
787
- });
788
- const response = await this.request<{ users: User[] }>('GET', endpoint);
789
- return response.users ?? [];
669
+ return this.users.getUsers(limit, offset, projectId);
790
670
  }
791
671
 
792
672
  /**
@@ -794,297 +674,318 @@ export class TestRailClient extends TestRailClientCore {
794
674
  * @throws {TestRailApiError} When the API request fails
795
675
  */
796
676
  async getCurrentUser(): Promise<User> {
797
- return this.request<User>('GET', 'get_current_user');
677
+ return this.users.getCurrentUser();
678
+ }
679
+
680
+ /**
681
+ * Add user.
682
+ *
683
+ * @param payload - Request payload for this operation.
684
+ * @throws {TestRailApiError} When the API request fails
685
+ */
686
+ async addUser(payload: AddUserPayload): Promise<User> {
687
+ return this.users.addUser(payload);
688
+ }
689
+
690
+ /**
691
+ * Update user.
692
+ *
693
+ * @param userId - User identifier.
694
+ * @param payload - Request payload for this operation.
695
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
696
+ * @throws {TestRailApiError} When the API request fails
697
+ */
698
+ async updateUser(userId: number, payload: UpdateUserPayload): Promise<User> {
699
+ return this.users.updateUser(userId, payload);
798
700
  }
799
701
 
800
702
  // ── Statuses ──────────────────────────────────────────────────────────────
801
703
 
802
704
  /**
803
- * Get all test statuses.
705
+ * Get statuses.
804
706
  * @throws {TestRailApiError} When the API request fails
805
707
  */
806
708
  async getStatuses(): Promise<Status[]> {
807
- return this.request<Status[]>('GET', 'get_statuses');
709
+ return this.metadata.getStatuses();
808
710
  }
809
711
 
810
712
  // ── Priorities ────────────────────────────────────────────────────────────
811
713
 
812
714
  /**
813
- * Get all case priorities.
715
+ * Get priorities.
814
716
  * @throws {TestRailApiError} When the API request fails
815
717
  */
816
718
  async getPriorities(): Promise<Priority[]> {
817
- return this.request<Priority[]>('GET', 'get_priorities');
719
+ return this.metadata.getPriorities();
818
720
  }
819
721
 
820
722
  // ── Result Fields ─────────────────────────────────────────────────────────
821
723
 
822
724
  /**
823
- * Get all available custom result fields.
725
+ * Get result fields.
824
726
  * @throws {TestRailApiError} When the API request fails
825
727
  */
826
728
  async getResultFields(): Promise<ResultField[]> {
827
- return this.request<ResultField[]>('GET', 'get_result_fields');
729
+ return this.metadata.getResultFields();
828
730
  }
829
731
 
830
732
  // ── Case Fields & Types ───────────────────────────────────────────────────
831
733
 
832
734
  /**
833
- * Get all available custom case fields.
735
+ * Get case fields.
834
736
  * @throws {TestRailApiError} When the API request fails
835
737
  */
836
738
  async getCaseFields(): Promise<CaseField[]> {
837
- return this.request<CaseField[]>('GET', 'get_case_fields');
739
+ return this.metadata.getCaseFields();
838
740
  }
839
741
 
840
742
  /**
841
- * Get all available case types.
743
+ * Get case types.
842
744
  * @throws {TestRailApiError} When the API request fails
843
745
  */
844
746
  async getCaseTypes(): Promise<CaseType[]> {
845
- return this.request<CaseType[]>('GET', 'get_case_types');
747
+ return this.metadata.getCaseTypes();
846
748
  }
847
749
 
848
750
  // ── Templates ─────────────────────────────────────────────────────────────
849
751
 
850
752
  /**
851
- * Get all available case templates for a project (requires TestRail 5.2+).
852
- * @throws {TestRailValidationError} When projectId is invalid
753
+ * Get templates.
754
+ *
755
+ * @param projectId - Project identifier.
756
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
853
757
  * @throws {TestRailApiError} When the API request fails
854
758
  */
855
759
  async getTemplates(projectId: number): Promise<Template[]> {
856
- this.validateId(projectId, 'projectId');
857
- return this.request<Template[]>('GET', `get_templates/${projectId}`);
760
+ return this.metadata.getTemplates(projectId);
858
761
  }
859
762
 
860
763
  // ── Configurations ────────────────────────────────────────────────────────
861
764
 
862
765
  /**
863
- * Get all configuration groups and their configurations for a project.
864
- * @throws {TestRailValidationError} When projectId is invalid
766
+ * Get configurations.
767
+ *
768
+ * @param projectId - Project identifier.
769
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
865
770
  * @throws {TestRailApiError} When the API request fails
866
771
  */
867
772
  async getConfigurations(projectId: number): Promise<ConfigurationGroup[]> {
868
- this.validateId(projectId, 'projectId');
869
- return this.request<ConfigurationGroup[]>('GET', `get_configs/${projectId}`);
773
+ return this.configurations.getConfigurations(projectId);
870
774
  }
871
775
 
872
776
  /**
873
- * Add a new configuration group to a project.
874
- * @throws {TestRailValidationError} When projectId is invalid
777
+ * Add configuration group.
778
+ *
779
+ * @param projectId - Project identifier.
780
+ * @param payload - Request payload for this operation.
781
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
875
782
  * @throws {TestRailApiError} When the API request fails
876
783
  */
877
784
  async addConfigurationGroup(projectId: number, payload: AddConfigurationGroupPayload): Promise<ConfigurationGroup> {
878
- this.validateId(projectId, 'projectId');
879
- return this.request<ConfigurationGroup>('POST', `add_config_group/${projectId}`, payload);
785
+ return this.configurations.addConfigurationGroup(projectId, payload);
880
786
  }
881
787
 
882
788
  /**
883
- * Update an existing configuration group.
884
- * @throws {TestRailValidationError} When configGroupId is invalid
789
+ * Update configuration group.
790
+ *
791
+ * @param configGroupId - Config group identifier.
792
+ * @param payload - Request payload for this operation.
793
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
885
794
  * @throws {TestRailApiError} When the API request fails
886
795
  */
887
796
  async updateConfigurationGroup(
888
797
  configGroupId: number,
889
798
  payload: UpdateConfigurationGroupPayload,
890
799
  ): Promise<ConfigurationGroup> {
891
- this.validateId(configGroupId, 'configGroupId');
892
- return this.request<ConfigurationGroup>('POST', `update_config_group/${configGroupId}`, payload);
800
+ return this.configurations.updateConfigurationGroup(configGroupId, payload);
893
801
  }
894
802
 
895
803
  /**
896
- * Delete an existing configuration group and all its configurations.
897
- * @throws {TestRailValidationError} When configGroupId is invalid
804
+ * Delete configuration group.
805
+ *
806
+ * @param configGroupId - Config group identifier.
807
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
898
808
  * @throws {TestRailApiError} When the API request fails
899
809
  */
900
810
  async deleteConfigurationGroup(configGroupId: number): Promise<void> {
901
- this.validateId(configGroupId, 'configGroupId');
902
- await this.request<void>('POST', `delete_config_group/${configGroupId}`);
811
+ return this.configurations.deleteConfigurationGroup(configGroupId);
903
812
  }
904
813
 
905
814
  /**
906
- * Add a new configuration to a configuration group.
907
- * @throws {TestRailValidationError} When configGroupId is invalid
815
+ * Add configuration.
816
+ *
817
+ * @param configGroupId - Config group identifier.
818
+ * @param payload - Request payload for this operation.
819
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
908
820
  * @throws {TestRailApiError} When the API request fails
909
821
  */
910
822
  async addConfiguration(configGroupId: number, payload: AddConfigurationPayload): Promise<Configuration> {
911
- this.validateId(configGroupId, 'configGroupId');
912
- return this.request<Configuration>('POST', `add_config/${configGroupId}`, payload);
823
+ return this.configurations.addConfiguration(configGroupId, payload);
913
824
  }
914
825
 
915
826
  /**
916
- * Update an existing configuration.
917
- * @throws {TestRailValidationError} When configId is invalid
827
+ * Update configuration.
828
+ *
829
+ * @param configId - Config identifier.
830
+ * @param payload - Request payload for this operation.
831
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
918
832
  * @throws {TestRailApiError} When the API request fails
919
833
  */
920
834
  async updateConfiguration(configId: number, payload: UpdateConfigurationPayload): Promise<Configuration> {
921
- this.validateId(configId, 'configId');
922
- return this.request<Configuration>('POST', `update_config/${configId}`, payload);
835
+ return this.configurations.updateConfiguration(configId, payload);
923
836
  }
924
837
 
925
838
  /**
926
- * Delete an existing configuration.
927
- * @throws {TestRailValidationError} When configId is invalid
839
+ * Delete configuration.
840
+ *
841
+ * @param configId - Config identifier.
842
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
928
843
  * @throws {TestRailApiError} When the API request fails
929
844
  */
930
845
  async deleteConfiguration(configId: number): Promise<void> {
931
- this.validateId(configId, 'configId');
932
- await this.request<void>('POST', `delete_config/${configId}`);
933
- }
934
-
935
- // ── User Management (TASK-024, requires TestRail 7.3+) ────────────────────
936
-
937
- /**
938
- * Create a new TestRail user (requires TestRail 7.3+).
939
- * @throws {TestRailApiError} When the API request fails
940
- */
941
- async addUser(payload: AddUserPayload): Promise<User> {
942
- return this.request<User>('POST', 'add_user', payload);
943
- }
944
-
945
- /**
946
- * Update an existing TestRail user (requires TestRail 7.3+).
947
- * @throws {TestRailValidationError} When userId is invalid
948
- * @throws {TestRailApiError} When the API request fails
949
- */
950
- async updateUser(userId: number, payload: UpdateUserPayload): Promise<User> {
951
- this.validateId(userId, 'userId');
952
- return this.request<User>('POST', `update_user/${userId}`, payload);
846
+ return this.configurations.deleteConfiguration(configId);
953
847
  }
954
848
 
955
- // ── Roles (TASK-025, requires TestRail 7.3+) ──────────────────────────────
849
+ // ── Roles ─────────────────────────────────────────────────────────────────
956
850
 
957
851
  /**
958
- * Get all available user roles (requires TestRail 7.3+).
852
+ * Get roles.
959
853
  * @throws {TestRailApiError} When the API request fails
960
854
  */
961
855
  async getRoles(): Promise<Role[]> {
962
- return this.request<Role[]>('GET', 'get_roles');
856
+ return this.metadata.getRoles();
963
857
  }
964
858
 
965
- // ── Groups (TASK-026, requires TestRail 7.5+) ─────────────────────────────
859
+ // ── Groups ────────────────────────────────────────────────────────────────
966
860
 
967
861
  /**
968
- * Get a single group by ID (requires TestRail 7.5+).
969
- * @throws {TestRailValidationError} When groupId is invalid
862
+ * Get group.
863
+ *
864
+ * @param groupId - Group identifier.
865
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
970
866
  * @throws {TestRailApiError} When the API request fails
971
867
  */
972
868
  async getGroup(groupId: number): Promise<Group> {
973
- this.validateId(groupId, 'groupId');
974
- return this.request<Group>('GET', `get_group/${groupId}`);
869
+ return this.users.getGroup(groupId);
975
870
  }
976
871
 
977
872
  /**
978
- * Get all groups (requires TestRail 7.5+).
873
+ * Get groups.
979
874
  * @throws {TestRailApiError} When the API request fails
980
875
  */
981
876
  async getGroups(): Promise<Group[]> {
982
- return this.request<Group[]>('GET', 'get_groups');
877
+ return this.users.getGroups();
983
878
  }
984
879
 
985
880
  /**
986
- * Create a new group (requires TestRail 7.5+).
881
+ * Add group.
882
+ *
883
+ * @param payload - Request payload for this operation.
987
884
  * @throws {TestRailApiError} When the API request fails
988
885
  */
989
886
  async addGroup(payload: AddGroupPayload): Promise<Group> {
990
- return this.request<Group>('POST', 'add_group', payload);
887
+ return this.users.addGroup(payload);
991
888
  }
992
889
 
993
890
  /**
994
- * Update an existing group (requires TestRail 7.5+).
995
- * @throws {TestRailValidationError} When groupId is invalid
891
+ * Update group.
892
+ *
893
+ * @param groupId - Group identifier.
894
+ * @param payload - Request payload for this operation.
895
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
996
896
  * @throws {TestRailApiError} When the API request fails
997
897
  */
998
898
  async updateGroup(groupId: number, payload: UpdateGroupPayload): Promise<Group> {
999
- this.validateId(groupId, 'groupId');
1000
- return this.request<Group>('POST', `update_group/${groupId}`, payload);
899
+ return this.users.updateGroup(groupId, payload);
1001
900
  }
1002
901
 
1003
902
  /**
1004
- * Delete a group (requires TestRail 7.5+).
1005
- * @throws {TestRailValidationError} When groupId is invalid
903
+ * Delete group.
904
+ *
905
+ * @param groupId - Group identifier.
906
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1006
907
  * @throws {TestRailApiError} When the API request fails
1007
908
  */
1008
909
  async deleteGroup(groupId: number): Promise<void> {
1009
- this.validateId(groupId, 'groupId');
1010
- await this.request<void>('POST', `delete_group/${groupId}`);
910
+ return this.users.deleteGroup(groupId);
1011
911
  }
1012
912
 
1013
- // ── Attachments (TASK-027) ────────────────────────────────────────────────
913
+ // ── Attachments ───────────────────────────────────────────────────────────
1014
914
 
1015
915
  /**
1016
- * Get all attachments for a test case.
1017
- * @throws {TestRailValidationError} When caseId is invalid
916
+ * Get attachments for case.
917
+ *
918
+ * @param caseId - Case identifier.
919
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1018
920
  * @throws {TestRailApiError} When the API request fails
1019
921
  */
1020
922
  async getAttachmentsForCase(caseId: number): Promise<Attachment[]> {
1021
- this.validateId(caseId, 'caseId');
1022
- const response = await this.request<{ attachments: Attachment[] }>('GET', `get_attachments_for_case/${caseId}`);
1023
- return response.attachments ?? [];
923
+ return this.attachments.getAttachmentsForCase(caseId);
1024
924
  }
1025
925
 
1026
926
  /**
1027
- * Get all attachments for a test run.
1028
- * @throws {TestRailValidationError} When runId is invalid
927
+ * Get attachments for run.
928
+ *
929
+ * @param runId - Run identifier.
930
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1029
931
  * @throws {TestRailApiError} When the API request fails
1030
932
  */
1031
933
  async getAttachmentsForRun(runId: number): Promise<Attachment[]> {
1032
- this.validateId(runId, 'runId');
1033
- const response = await this.request<{ attachments: Attachment[] }>('GET', `get_attachments_for_run/${runId}`);
1034
- return response.attachments ?? [];
934
+ return this.attachments.getAttachmentsForRun(runId);
1035
935
  }
1036
936
 
1037
937
  /**
1038
- * Get all attachments for a test.
1039
- * @throws {TestRailValidationError} When testId is invalid
938
+ * Get attachments for test.
939
+ *
940
+ * @param testId - Test identifier.
941
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1040
942
  * @throws {TestRailApiError} When the API request fails
1041
943
  */
1042
944
  async getAttachmentsForTest(testId: number): Promise<Attachment[]> {
1043
- this.validateId(testId, 'testId');
1044
- const response = await this.request<{ attachments: Attachment[] }>('GET', `get_attachments_for_test/${testId}`);
1045
- return response.attachments ?? [];
945
+ return this.attachments.getAttachmentsForTest(testId);
1046
946
  }
1047
947
 
1048
948
  /**
1049
- * Get all attachments for a test plan.
1050
- * @throws {TestRailValidationError} When planId is invalid
949
+ * Get attachments for plan.
950
+ *
951
+ * @param planId - Plan identifier.
952
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1051
953
  * @throws {TestRailApiError} When the API request fails
1052
954
  */
1053
955
  async getAttachmentsForPlan(planId: number): Promise<Attachment[]> {
1054
- this.validateId(planId, 'planId');
1055
- const response = await this.request<{ attachments: Attachment[] }>('GET', `get_attachments_for_plan/${planId}`);
1056
- return response.attachments ?? [];
956
+ return this.attachments.getAttachmentsForPlan(planId);
1057
957
  }
1058
958
 
1059
959
  /**
1060
- * Get all attachments for a specific plan entry.
1061
- * @throws {TestRailValidationError} When planId or entryId is invalid
960
+ * Get attachments for plan entry.
961
+ *
962
+ * @param planId - Plan identifier.
963
+ * @param entryId - Plan entry identifier.
964
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1062
965
  * @throws {TestRailApiError} When the API request fails
1063
966
  */
1064
967
  async getAttachmentsForPlanEntry(planId: number, entryId: number): Promise<Attachment[]> {
1065
- this.validateId(planId, 'planId');
1066
- this.validateId(entryId, 'entryId');
1067
- const response = await this.request<{ attachments: Attachment[] }>(
1068
- 'GET',
1069
- `get_attachments_for_plan_entry/${planId}/${entryId}`,
1070
- );
1071
- return response.attachments ?? [];
968
+ return this.attachments.getAttachmentsForPlanEntry(planId, entryId);
1072
969
  }
1073
970
 
1074
971
  /**
1075
- * Download the raw binary content of an attachment.
1076
- * @param attachmentId - The attachment ID (numeric)
1077
- * @throws {TestRailValidationError} When attachmentId is invalid
972
+ * Download an attachment by ID.
973
+ *
974
+ * @param attachmentId - Attachment identifier.
975
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1078
976
  * @throws {TestRailApiError} When the API request fails
1079
977
  */
1080
978
  async getAttachment(attachmentId: number): Promise<ArrayBuffer> {
1081
- this.validateId(attachmentId, 'attachmentId');
1082
- return this.requestBinary(`get_attachment/${attachmentId}`);
979
+ return this.attachments.getAttachment(attachmentId);
1083
980
  }
1084
981
 
1085
982
  /**
1086
- * Upload a file attachment to a test case.
1087
- * @throws {TestRailValidationError} When caseId is invalid
983
+ * Add attachment to case.
984
+ *
985
+ * @param caseId - Case identifier.
986
+ * @param file - Attachment contents to upload.
987
+ * @param filename - Filename to send with the uploaded attachment.
988
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1088
989
  * @throws {TestRailApiError} When the API request fails
1089
990
  */
1090
991
  async addAttachmentToCase(
@@ -1092,13 +993,16 @@ export class TestRailClient extends TestRailClientCore {
1092
993
  file: globalThis.Blob | Uint8Array | globalThis.File,
1093
994
  filename: string,
1094
995
  ): Promise<Attachment> {
1095
- this.validateId(caseId, 'caseId');
1096
- return this.requestMultipart<Attachment>(`add_attachment_to_case/${caseId}`, file, filename);
996
+ return this.attachments.addAttachmentToCase(caseId, file, filename);
1097
997
  }
1098
998
 
1099
999
  /**
1100
- * Upload a file attachment to a test result.
1101
- * @throws {TestRailValidationError} When resultId is invalid
1000
+ * Add attachment to result.
1001
+ *
1002
+ * @param resultId - Result identifier.
1003
+ * @param file - Attachment contents to upload.
1004
+ * @param filename - Filename to send with the uploaded attachment.
1005
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1102
1006
  * @throws {TestRailApiError} When the API request fails
1103
1007
  */
1104
1008
  async addAttachmentToResult(
@@ -1106,13 +1010,16 @@ export class TestRailClient extends TestRailClientCore {
1106
1010
  file: globalThis.Blob | Uint8Array | globalThis.File,
1107
1011
  filename: string,
1108
1012
  ): Promise<Attachment> {
1109
- this.validateId(resultId, 'resultId');
1110
- return this.requestMultipart<Attachment>(`add_attachment_to_result/${resultId}`, file, filename);
1013
+ return this.attachments.addAttachmentToResult(resultId, file, filename);
1111
1014
  }
1112
1015
 
1113
1016
  /**
1114
- * Upload a file attachment to a test run.
1115
- * @throws {TestRailValidationError} When runId is invalid
1017
+ * Add attachment to run.
1018
+ *
1019
+ * @param runId - Run identifier.
1020
+ * @param file - Attachment contents to upload.
1021
+ * @param filename - Filename to send with the uploaded attachment.
1022
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1116
1023
  * @throws {TestRailApiError} When the API request fails
1117
1024
  */
1118
1025
  async addAttachmentToRun(
@@ -1120,13 +1027,16 @@ export class TestRailClient extends TestRailClientCore {
1120
1027
  file: globalThis.Blob | Uint8Array | globalThis.File,
1121
1028
  filename: string,
1122
1029
  ): Promise<Attachment> {
1123
- this.validateId(runId, 'runId');
1124
- return this.requestMultipart<Attachment>(`add_attachment_to_run/${runId}`, file, filename);
1030
+ return this.attachments.addAttachmentToRun(runId, file, filename);
1125
1031
  }
1126
1032
 
1127
1033
  /**
1128
- * Upload a file attachment to a test plan (requires TestRail 6.5.2+).
1129
- * @throws {TestRailValidationError} When planId is invalid
1034
+ * Add attachment to plan.
1035
+ *
1036
+ * @param planId - Plan identifier.
1037
+ * @param file - Attachment contents to upload.
1038
+ * @param filename - Filename to send with the uploaded attachment.
1039
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1130
1040
  * @throws {TestRailApiError} When the API request fails
1131
1041
  */
1132
1042
  async addAttachmentToPlan(
@@ -1134,13 +1044,17 @@ export class TestRailClient extends TestRailClientCore {
1134
1044
  file: globalThis.Blob | Uint8Array | globalThis.File,
1135
1045
  filename: string,
1136
1046
  ): Promise<Attachment> {
1137
- this.validateId(planId, 'planId');
1138
- return this.requestMultipart<Attachment>(`add_attachment_to_plan/${planId}`, file, filename);
1047
+ return this.attachments.addAttachmentToPlan(planId, file, filename);
1139
1048
  }
1140
1049
 
1141
1050
  /**
1142
- * Upload a file attachment to a specific plan entry (requires TestRail 6.5.2+).
1143
- * @throws {TestRailValidationError} When planId or entryId is invalid
1051
+ * Add attachment to plan entry.
1052
+ *
1053
+ * @param planId - Plan identifier.
1054
+ * @param entryId - Plan entry identifier.
1055
+ * @param file - Attachment contents to upload.
1056
+ * @param filename - Filename to send with the uploaded attachment.
1057
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1144
1058
  * @throws {TestRailApiError} When the API request fails
1145
1059
  */
1146
1060
  async addAttachmentToPlanEntry(
@@ -1149,190 +1063,207 @@ export class TestRailClient extends TestRailClientCore {
1149
1063
  file: globalThis.Blob | Uint8Array | globalThis.File,
1150
1064
  filename: string,
1151
1065
  ): Promise<Attachment> {
1152
- this.validateId(planId, 'planId');
1153
- this.validateId(entryId, 'entryId');
1154
- return this.requestMultipart<Attachment>(`add_attachment_to_plan_entry/${planId}/${entryId}`, file, filename);
1066
+ return this.attachments.addAttachmentToPlanEntry(planId, entryId, file, filename);
1155
1067
  }
1156
1068
 
1157
1069
  /**
1158
- * Delete an attachment by ID.
1159
- * @throws {TestRailValidationError} When attachmentId is invalid
1070
+ * Delete attachment.
1071
+ *
1072
+ * @param attachmentId - Attachment identifier.
1073
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1160
1074
  * @throws {TestRailApiError} When the API request fails
1161
1075
  */
1162
1076
  async deleteAttachment(attachmentId: number): Promise<void> {
1163
- this.validateId(attachmentId, 'attachmentId');
1164
- await this.request<void>('POST', `delete_attachment/${attachmentId}`);
1077
+ return this.attachments.deleteAttachment(attachmentId);
1165
1078
  }
1166
1079
 
1167
- // ── Shared Steps (TASK-028, requires TestRail 7.0+) ───────────────────────
1080
+ // ── Shared Steps ──────────────────────────────────────────────────────────
1168
1081
 
1169
1082
  /**
1170
- * Get a single shared step by ID (requires TestRail 7.0+).
1171
- * @throws {TestRailValidationError} When sharedStepId is invalid
1083
+ * Get shared step.
1084
+ *
1085
+ * @param sharedStepId - Shared step identifier.
1086
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1172
1087
  * @throws {TestRailApiError} When the API request fails
1173
1088
  */
1174
1089
  async getSharedStep(sharedStepId: number): Promise<SharedStep> {
1175
- this.validateId(sharedStepId, 'sharedStepId');
1176
- return this.request<SharedStep>('GET', `get_shared_step/${sharedStepId}`);
1090
+ return this.sharedSteps.getSharedStep(sharedStepId);
1177
1091
  }
1178
1092
 
1179
1093
  /**
1180
- * Get all shared steps for a project (requires TestRail 7.0+).
1181
- * @throws {TestRailValidationError} When projectId is invalid
1094
+ * Get shared steps.
1095
+ *
1096
+ * @param projectId - Project identifier.
1097
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1182
1098
  * @throws {TestRailApiError} When the API request fails
1183
1099
  */
1184
1100
  async getSharedSteps(projectId: number): Promise<SharedStep[]> {
1185
- this.validateId(projectId, 'projectId');
1186
- return this.request<SharedStep[]>('GET', `get_shared_steps/${projectId}`);
1101
+ return this.sharedSteps.getSharedSteps(projectId);
1187
1102
  }
1188
1103
 
1189
1104
  /**
1190
- * Create a new shared step in a project (requires TestRail 7.0+).
1191
- * @throws {TestRailValidationError} When projectId is invalid
1105
+ * Add shared step.
1106
+ *
1107
+ * @param projectId - Project identifier.
1108
+ * @param payload - Request payload for this operation.
1109
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1192
1110
  * @throws {TestRailApiError} When the API request fails
1193
1111
  */
1194
1112
  async addSharedStep(projectId: number, payload: AddSharedStepPayload): Promise<SharedStep> {
1195
- this.validateId(projectId, 'projectId');
1196
- return this.request<SharedStep>('POST', `add_shared_step/${projectId}`, payload);
1113
+ return this.sharedSteps.addSharedStep(projectId, payload);
1197
1114
  }
1198
1115
 
1199
1116
  /**
1200
- * Update an existing shared step (requires TestRail 7.0+).
1201
- * @throws {TestRailValidationError} When sharedStepId is invalid
1117
+ * Update shared step.
1118
+ *
1119
+ * @param sharedStepId - Shared step identifier.
1120
+ * @param payload - Request payload for this operation.
1121
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1202
1122
  * @throws {TestRailApiError} When the API request fails
1203
1123
  */
1204
1124
  async updateSharedStep(sharedStepId: number, payload: UpdateSharedStepPayload): Promise<SharedStep> {
1205
- this.validateId(sharedStepId, 'sharedStepId');
1206
- return this.request<SharedStep>('POST', `update_shared_step/${sharedStepId}`, payload);
1125
+ return this.sharedSteps.updateSharedStep(sharedStepId, payload);
1207
1126
  }
1208
1127
 
1209
1128
  /**
1210
- * Delete a shared step (requires TestRail 7.0+).
1211
- * @throws {TestRailValidationError} When sharedStepId is invalid
1129
+ * Delete shared step.
1130
+ *
1131
+ * @param sharedStepId - Shared step identifier.
1132
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1212
1133
  * @throws {TestRailApiError} When the API request fails
1213
1134
  */
1214
1135
  async deleteSharedStep(sharedStepId: number): Promise<void> {
1215
- this.validateId(sharedStepId, 'sharedStepId');
1216
- await this.request<void>('POST', `delete_shared_step/${sharedStepId}`);
1136
+ return this.sharedSteps.deleteSharedStep(sharedStepId);
1217
1137
  }
1218
1138
 
1219
- // ── Variables (TASK-029) ──────────────────────────────────────────────────
1139
+ // ── Variables ─────────────────────────────────────────────────────────────
1220
1140
 
1221
1141
  /**
1222
- * Get all variables for a project.
1223
- * @throws {TestRailValidationError} When projectId is invalid
1142
+ * Get variables.
1143
+ *
1144
+ * @param projectId - Project identifier.
1145
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1224
1146
  * @throws {TestRailApiError} When the API request fails
1225
1147
  */
1226
1148
  async getVariables(projectId: number): Promise<Variable[]> {
1227
- this.validateId(projectId, 'projectId');
1228
- return this.request<Variable[]>('GET', `get_variables/${projectId}`);
1149
+ return this.variables.getVariables(projectId);
1229
1150
  }
1230
1151
 
1231
1152
  /**
1232
- * Create a new variable in a project.
1233
- * @throws {TestRailValidationError} When projectId is invalid
1153
+ * Add variable.
1154
+ *
1155
+ * @param projectId - Project identifier.
1156
+ * @param payload - Request payload for this operation.
1157
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1234
1158
  * @throws {TestRailApiError} When the API request fails
1235
1159
  */
1236
1160
  async addVariable(projectId: number, payload: AddVariablePayload): Promise<Variable> {
1237
- this.validateId(projectId, 'projectId');
1238
- return this.request<Variable>('POST', `add_variable/${projectId}`, payload);
1161
+ return this.variables.addVariable(projectId, payload);
1239
1162
  }
1240
1163
 
1241
1164
  /**
1242
- * Update an existing variable.
1243
- * @throws {TestRailValidationError} When variableId is invalid
1165
+ * Update variable.
1166
+ *
1167
+ * @param variableId - Variable identifier.
1168
+ * @param payload - Request payload for this operation.
1169
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1244
1170
  * @throws {TestRailApiError} When the API request fails
1245
1171
  */
1246
1172
  async updateVariable(variableId: number, payload: UpdateVariablePayload): Promise<Variable> {
1247
- this.validateId(variableId, 'variableId');
1248
- return this.request<Variable>('POST', `update_variable/${variableId}`, payload);
1173
+ return this.variables.updateVariable(variableId, payload);
1249
1174
  }
1250
1175
 
1251
1176
  /**
1252
- * Delete a variable.
1253
- * @throws {TestRailValidationError} When variableId is invalid
1177
+ * Delete variable.
1178
+ *
1179
+ * @param variableId - Variable identifier.
1180
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1254
1181
  * @throws {TestRailApiError} When the API request fails
1255
1182
  */
1256
1183
  async deleteVariable(variableId: number): Promise<void> {
1257
- this.validateId(variableId, 'variableId');
1258
- await this.request<void>('POST', `delete_variable/${variableId}`);
1184
+ return this.variables.deleteVariable(variableId);
1259
1185
  }
1260
1186
 
1261
- // ── Datasets (TASK-030) ───────────────────────────────────────────────────
1187
+ // ── Datasets ──────────────────────────────────────────────────────────────
1262
1188
 
1263
1189
  /**
1264
- * Get a single dataset by ID.
1265
- * @throws {TestRailValidationError} When datasetId is invalid
1190
+ * Get dataset.
1191
+ *
1192
+ * @param datasetId - Dataset identifier.
1193
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1266
1194
  * @throws {TestRailApiError} When the API request fails
1267
1195
  */
1268
1196
  async getDataset(datasetId: number): Promise<Dataset> {
1269
- this.validateId(datasetId, 'datasetId');
1270
- return this.request<Dataset>('GET', `get_dataset/${datasetId}`);
1197
+ return this.datasets.getDataset(datasetId);
1271
1198
  }
1272
1199
 
1273
1200
  /**
1274
- * Get all datasets for a project.
1275
- * @throws {TestRailValidationError} When projectId is invalid
1201
+ * Get datasets.
1202
+ *
1203
+ * @param projectId - Project identifier.
1204
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1276
1205
  * @throws {TestRailApiError} When the API request fails
1277
1206
  */
1278
1207
  async getDatasets(projectId: number): Promise<Dataset[]> {
1279
- this.validateId(projectId, 'projectId');
1280
- return this.request<Dataset[]>('GET', `get_datasets/${projectId}`);
1208
+ return this.datasets.getDatasets(projectId);
1281
1209
  }
1282
1210
 
1283
1211
  /**
1284
- * Create a new dataset in a project.
1285
- * @throws {TestRailValidationError} When projectId is invalid
1212
+ * Add dataset.
1213
+ *
1214
+ * @param projectId - Project identifier.
1215
+ * @param payload - Request payload for this operation.
1216
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1286
1217
  * @throws {TestRailApiError} When the API request fails
1287
1218
  */
1288
1219
  async addDataset(projectId: number, payload: AddDatasetPayload): Promise<Dataset> {
1289
- this.validateId(projectId, 'projectId');
1290
- return this.request<Dataset>('POST', `add_dataset/${projectId}`, payload);
1220
+ return this.datasets.addDataset(projectId, payload);
1291
1221
  }
1292
1222
 
1293
1223
  /**
1294
- * Update an existing dataset.
1295
- * @throws {TestRailValidationError} When datasetId is invalid
1224
+ * Update dataset.
1225
+ *
1226
+ * @param datasetId - Dataset identifier.
1227
+ * @param payload - Request payload for this operation.
1228
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1296
1229
  * @throws {TestRailApiError} When the API request fails
1297
1230
  */
1298
1231
  async updateDataset(datasetId: number, payload: UpdateDatasetPayload): Promise<Dataset> {
1299
- this.validateId(datasetId, 'datasetId');
1300
- return this.request<Dataset>('POST', `update_dataset/${datasetId}`, payload);
1232
+ return this.datasets.updateDataset(datasetId, payload);
1301
1233
  }
1302
1234
 
1303
1235
  /**
1304
- * Delete a dataset.
1305
- * @throws {TestRailValidationError} When datasetId is invalid
1236
+ * Delete dataset.
1237
+ *
1238
+ * @param datasetId - Dataset identifier.
1239
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1306
1240
  * @throws {TestRailApiError} When the API request fails
1307
1241
  */
1308
1242
  async deleteDataset(datasetId: number): Promise<void> {
1309
- this.validateId(datasetId, 'datasetId');
1310
- await this.request<void>('POST', `delete_dataset/${datasetId}`);
1243
+ return this.datasets.deleteDataset(datasetId);
1311
1244
  }
1312
1245
 
1313
- // ── Reports (TASK-031) ────────────────────────────────────────────────────
1246
+ // ── Reports ───────────────────────────────────────────────────────────────
1314
1247
 
1315
1248
  /**
1316
- * Get all available report templates for a project.
1317
- * @throws {TestRailValidationError} When projectId is invalid
1249
+ * Get reports.
1250
+ *
1251
+ * @param projectId - Project identifier.
1252
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1318
1253
  * @throws {TestRailApiError} When the API request fails
1319
1254
  */
1320
1255
  async getReports(projectId: number): Promise<Report[]> {
1321
- this.validateId(projectId, 'projectId');
1322
- return this.request<Report[]>('GET', `get_reports/${projectId}`);
1256
+ return this.reports.getReports(projectId);
1323
1257
  }
1324
1258
 
1325
1259
  /**
1326
- * Execute a report template and return URLs to the generated output.
1327
- * @throws {TestRailValidationError} When reportTemplateId is invalid
1260
+ * Run report.
1261
+ *
1262
+ * @param reportTemplateId - Report template identifier.
1263
+ * @throws {TestRailValidationError} When identifiers or request parameters are invalid
1328
1264
  * @throws {TestRailApiError} When the API request fails
1329
1265
  */
1330
1266
  async runReport(reportTemplateId: number): Promise<ReportResult> {
1331
- this.validateId(reportTemplateId, 'reportTemplateId');
1332
- return this.request<ReportResult>('GET', `run_report/${reportTemplateId}`);
1333
- }
1334
-
1335
- private serializeIdList(ids?: number[]): string | undefined {
1336
- return ids !== undefined && ids.length > 0 ? ids.join(',') : undefined;
1267
+ return this.reports.runReport(reportTemplateId);
1337
1268
  }
1338
1269
  }