jira-datacenter-api-client 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,863 @@
1
+ // src/security/Security.ts
2
+ function toBase64(value) {
3
+ if (typeof btoa !== "undefined") {
4
+ return btoa(value);
5
+ }
6
+ return Buffer.from(value).toString("base64");
7
+ }
8
+ var Security = class {
9
+ /**
10
+ * Creates a new Security instance with Basic Authentication credentials.
11
+ *
12
+ * @param apiUrl - The base URL of the Jira Data Center instance (e.g., `https://jira.example.com`).
13
+ * Must be a valid URL; throws if it cannot be parsed.
14
+ * @param user - The username to authenticate with
15
+ * @param token - The personal access token or password to authenticate with
16
+ *
17
+ * @throws {TypeError} If `apiUrl` is not a valid URL
18
+ */
19
+ constructor(apiUrl, user, token) {
20
+ if (!URL.canParse(apiUrl)) {
21
+ throw new TypeError(`Invalid apiUrl: "${apiUrl}" is not a valid URL`);
22
+ }
23
+ this.apiUrl = apiUrl.replace(/\/$/, "");
24
+ this.authorizationHeader = `Basic ${toBase64(`${user}:${token}`)}`;
25
+ }
26
+ /**
27
+ * Returns the base URL of the Jira Data Center instance, without a trailing slash.
28
+ *
29
+ * @returns The API base URL
30
+ */
31
+ getApiUrl() {
32
+ return this.apiUrl;
33
+ }
34
+ /**
35
+ * Returns the value of the `Authorization` header for Basic Authentication.
36
+ *
37
+ * @returns The Authorization header value in the format `Basic <base64-encoded-credentials>`
38
+ */
39
+ getAuthorizationHeader() {
40
+ return this.authorizationHeader;
41
+ }
42
+ /**
43
+ * Returns the full set of HTTP headers required for authenticated API requests.
44
+ *
45
+ * @returns An object containing `Authorization`, `Content-Type`, and `Accept` headers
46
+ */
47
+ getHeaders() {
48
+ return {
49
+ Authorization: this.authorizationHeader,
50
+ "Content-Type": "application/json",
51
+ Accept: "application/json"
52
+ };
53
+ }
54
+ };
55
+
56
+ // src/errors/JiraApiError.ts
57
+ var JiraApiError = class extends Error {
58
+ constructor(status, statusText) {
59
+ super(`Jira API error: ${status} ${statusText}`);
60
+ this.name = "JiraApiError";
61
+ this.status = status;
62
+ this.statusText = statusText;
63
+ }
64
+ };
65
+
66
+ // src/resources/IssueResource.ts
67
+ var IssueResource = class {
68
+ /** @internal */
69
+ constructor(request, issueIdOrKey) {
70
+ this.request = request;
71
+ this.basePath = `/issue/${issueIdOrKey}`;
72
+ }
73
+ /**
74
+ * Allows the resource to be awaited directly, resolving with the issue.
75
+ * Delegates to {@link IssueResource.get}.
76
+ */
77
+ then(onfulfilled, onrejected) {
78
+ return this.get().then(onfulfilled, onrejected);
79
+ }
80
+ /**
81
+ * Fetches the issue.
82
+ *
83
+ * `GET /rest/api/latest/issue/{issueIdOrKey}`
84
+ *
85
+ * @param params - Optional: `fields`, `expand`, `properties`, `updateHistory`
86
+ * @returns The issue object
87
+ */
88
+ async get(params) {
89
+ return this.request(
90
+ this.basePath,
91
+ params
92
+ );
93
+ }
94
+ /**
95
+ * Fetches comments for this issue.
96
+ *
97
+ * `GET /rest/api/latest/issue/{issueIdOrKey}/comment`
98
+ *
99
+ * @param params - Optional: `startAt`, `maxResults`, `orderBy`, `expand`
100
+ * @returns A paged response of comments
101
+ */
102
+ async comments(params) {
103
+ return this.request(
104
+ `${this.basePath}/comment`,
105
+ params
106
+ );
107
+ }
108
+ /**
109
+ * Fetches a single comment by ID.
110
+ *
111
+ * `GET /rest/api/latest/issue/{issueIdOrKey}/comment/{id}`
112
+ *
113
+ * @param commentId - The numeric comment ID
114
+ * @returns The comment object
115
+ */
116
+ async comment(commentId) {
117
+ return this.request(`${this.basePath}/comment/${commentId}`);
118
+ }
119
+ /**
120
+ * Fetches worklogs for this issue.
121
+ *
122
+ * `GET /rest/api/latest/issue/{issueIdOrKey}/worklog`
123
+ *
124
+ * @param params - Optional: `startAt`, `maxResults`, `expand`
125
+ * @returns A paged response of worklogs
126
+ */
127
+ async worklogs(params) {
128
+ return this.request(
129
+ `${this.basePath}/worklog`,
130
+ params
131
+ );
132
+ }
133
+ /**
134
+ * Fetches a single worklog by ID.
135
+ *
136
+ * `GET /rest/api/latest/issue/{issueIdOrKey}/worklog/{id}`
137
+ *
138
+ * @param worklogId - The numeric worklog ID
139
+ * @returns The worklog object
140
+ */
141
+ async worklog(worklogId) {
142
+ return this.request(`${this.basePath}/worklog/${worklogId}`);
143
+ }
144
+ /**
145
+ * Fetches the changelog for this issue.
146
+ *
147
+ * `GET /rest/api/latest/issue/{issueIdOrKey}/changelog`
148
+ *
149
+ * @param params - Optional: `startAt`, `maxResults`
150
+ * @returns A paged response of changelog entries
151
+ */
152
+ async changelog(params) {
153
+ return this.request(
154
+ `${this.basePath}/changelog`,
155
+ params
156
+ );
157
+ }
158
+ /**
159
+ * Fetches the available workflow transitions for this issue.
160
+ *
161
+ * `GET /rest/api/latest/issue/{issueIdOrKey}/transitions`
162
+ *
163
+ * @param params - Optional: `transitionId`, `expand`, `skipRemoteOnlyCondition`
164
+ * @returns An object containing the list of transitions
165
+ */
166
+ async transitions(params) {
167
+ return this.request(
168
+ `${this.basePath}/transitions`,
169
+ params
170
+ );
171
+ }
172
+ /**
173
+ * Fetches the remote links (external URLs) for this issue.
174
+ *
175
+ * `GET /rest/api/latest/issue/{issueIdOrKey}/remotelink`
176
+ *
177
+ * @param globalId - Optional global ID to filter by a specific remote link
178
+ * @returns An array of remote links
179
+ */
180
+ async remotelinks(globalId) {
181
+ return this.request(
182
+ `${this.basePath}/remotelink`,
183
+ globalId !== void 0 ? { globalId } : void 0
184
+ );
185
+ }
186
+ /**
187
+ * Fetches the vote information for this issue.
188
+ *
189
+ * `GET /rest/api/latest/issue/{issueIdOrKey}/votes`
190
+ *
191
+ * @returns The votes object
192
+ */
193
+ async votes() {
194
+ return this.request(`${this.basePath}/votes`);
195
+ }
196
+ /**
197
+ * Fetches the watcher information for this issue.
198
+ *
199
+ * `GET /rest/api/latest/issue/{issueIdOrKey}/watchers`
200
+ *
201
+ * @returns The watchers object
202
+ */
203
+ async watchers() {
204
+ return this.request(`${this.basePath}/watchers`);
205
+ }
206
+ };
207
+
208
+ // src/resources/ProjectResource.ts
209
+ var ProjectResource = class {
210
+ /** @internal */
211
+ constructor(request, projectIdOrKey) {
212
+ this.request = request;
213
+ this.basePath = `/project/${projectIdOrKey}`;
214
+ }
215
+ /**
216
+ * Allows the resource to be awaited directly, resolving with the project.
217
+ * Delegates to {@link ProjectResource.get}.
218
+ */
219
+ then(onfulfilled, onrejected) {
220
+ return this.get().then(onfulfilled, onrejected);
221
+ }
222
+ /**
223
+ * Fetches the project details.
224
+ *
225
+ * `GET /rest/api/latest/project/{projectIdOrKey}`
226
+ *
227
+ * @param params - Optional: `expand`
228
+ * @returns The project object
229
+ */
230
+ async get(params) {
231
+ return this.request(
232
+ this.basePath,
233
+ params
234
+ );
235
+ }
236
+ /**
237
+ * Fetches the components defined in this project.
238
+ *
239
+ * `GET /rest/api/latest/project/{projectIdOrKey}/components`
240
+ *
241
+ * @returns An array of project components
242
+ */
243
+ async components() {
244
+ return this.request(`${this.basePath}/components`);
245
+ }
246
+ /**
247
+ * Fetches the versions defined in this project.
248
+ *
249
+ * `GET /rest/api/latest/project/{projectIdOrKey}/versions`
250
+ *
251
+ * @param expand - Optional: `'operations'`
252
+ * @returns An array of project versions
253
+ */
254
+ async versions(expand) {
255
+ return this.request(
256
+ `${this.basePath}/versions`,
257
+ expand !== void 0 ? { expand } : void 0
258
+ );
259
+ }
260
+ /**
261
+ * Fetches the statuses available in this project, grouped by issue type.
262
+ *
263
+ * `GET /rest/api/latest/project/{projectIdOrKey}/statuses`
264
+ *
265
+ * @returns An array of issue type → statuses mappings
266
+ */
267
+ async statuses() {
268
+ return this.request(`${this.basePath}/statuses`);
269
+ }
270
+ /**
271
+ * Fetches the roles defined in this project.
272
+ *
273
+ * `GET /rest/api/latest/project/{projectIdOrKey}/role`
274
+ *
275
+ * @returns A record mapping role names to their resource URLs
276
+ */
277
+ async roles() {
278
+ return this.request(`${this.basePath}/role`);
279
+ }
280
+ /**
281
+ * Fetches a single role by ID, including the list of actors.
282
+ *
283
+ * `GET /rest/api/latest/project/{projectIdOrKey}/role/{id}`
284
+ *
285
+ * @param roleId - The numeric role ID
286
+ * @returns The project role object with actors
287
+ */
288
+ async role(roleId) {
289
+ return this.request(`${this.basePath}/role/${roleId}`);
290
+ }
291
+ };
292
+
293
+ // src/resources/SprintResource.ts
294
+ var SprintResource = class {
295
+ /** @internal */
296
+ constructor(request, agileApiPath, sprintId) {
297
+ this.request = request;
298
+ this.agileApiPath = agileApiPath;
299
+ this.basePath = `/sprint/${sprintId}`;
300
+ }
301
+ /**
302
+ * Allows the resource to be awaited directly, resolving with the sprint.
303
+ * Delegates to {@link SprintResource.get}.
304
+ */
305
+ then(onfulfilled, onrejected) {
306
+ return this.get().then(onfulfilled, onrejected);
307
+ }
308
+ /**
309
+ * Fetches the sprint details.
310
+ *
311
+ * `GET /rest/agile/latest/sprint/{sprintId}`
312
+ *
313
+ * @returns The sprint object
314
+ */
315
+ async get() {
316
+ return this.request(this.basePath, void 0, { apiPath: this.agileApiPath });
317
+ }
318
+ /**
319
+ * Fetches issues in this sprint.
320
+ *
321
+ * `GET /rest/agile/latest/sprint/{sprintId}/issue`
322
+ *
323
+ * @param params - Optional: `startAt`, `maxResults`, `jql`, `fields`, `expand`
324
+ * @returns A search response containing the sprint's issues
325
+ */
326
+ async issues(params) {
327
+ return this.request(
328
+ `${this.basePath}/issue`,
329
+ params,
330
+ { apiPath: this.agileApiPath }
331
+ );
332
+ }
333
+ };
334
+
335
+ // src/resources/BoardResource.ts
336
+ var BoardResource = class {
337
+ /** @internal */
338
+ constructor(request, agileApiPath, boardId) {
339
+ this.request = request;
340
+ this.agileApiPath = agileApiPath;
341
+ this.basePath = `/board/${boardId}`;
342
+ }
343
+ /**
344
+ * Allows the resource to be awaited directly, resolving with the board.
345
+ * Delegates to {@link BoardResource.get}.
346
+ */
347
+ then(onfulfilled, onrejected) {
348
+ return this.get().then(onfulfilled, onrejected);
349
+ }
350
+ /**
351
+ * Fetches the board details.
352
+ *
353
+ * `GET /rest/agile/latest/board/{boardId}`
354
+ *
355
+ * @returns The board object
356
+ */
357
+ async get() {
358
+ return this.request(this.basePath, void 0, { apiPath: this.agileApiPath });
359
+ }
360
+ /**
361
+ * Fetches sprints for this board.
362
+ *
363
+ * `GET /rest/agile/latest/board/{boardId}/sprint`
364
+ *
365
+ * @param params - Optional: `startAt`, `maxResults`, `state`
366
+ * @returns A paged response of sprints
367
+ */
368
+ async sprints(params) {
369
+ return this.request(
370
+ `${this.basePath}/sprint`,
371
+ params,
372
+ { apiPath: this.agileApiPath }
373
+ );
374
+ }
375
+ /**
376
+ * Fetches all issues on this board (across all sprints and the backlog).
377
+ *
378
+ * `GET /rest/agile/latest/board/{boardId}/issue`
379
+ *
380
+ * @param params - Optional: `startAt`, `maxResults`, `jql`, `fields`, `expand`
381
+ * @returns A search response containing the board's issues
382
+ */
383
+ async issues(params) {
384
+ return this.request(
385
+ `${this.basePath}/issue`,
386
+ params,
387
+ { apiPath: this.agileApiPath }
388
+ );
389
+ }
390
+ /**
391
+ * Fetches the backlog issues for this board.
392
+ *
393
+ * `GET /rest/agile/latest/board/{boardId}/backlog`
394
+ *
395
+ * @param params - Optional: `startAt`, `maxResults`, `jql`, `fields`, `expand`
396
+ * @returns A search response containing the backlog issues
397
+ */
398
+ async backlog(params) {
399
+ return this.request(
400
+ `${this.basePath}/backlog`,
401
+ params,
402
+ { apiPath: this.agileApiPath }
403
+ );
404
+ }
405
+ /**
406
+ * Returns a {@link SprintResource} for a given sprint ID.
407
+ *
408
+ * The returned resource can be awaited directly to fetch sprint info,
409
+ * or chained to access sprint issues.
410
+ *
411
+ * @param sprintId - The numeric sprint ID
412
+ * @returns A chainable sprint resource
413
+ *
414
+ * @example
415
+ * ```typescript
416
+ * const sprint = await jiraClient.board(42).sprint(10);
417
+ * const sprintIssues = await jiraClient.board(42).sprint(10).issues();
418
+ * ```
419
+ */
420
+ sprint(sprintId) {
421
+ return new SprintResource(this.request, this.agileApiPath, sprintId);
422
+ }
423
+ };
424
+
425
+ // src/JiraClient.ts
426
+ var JiraClient = class {
427
+ /**
428
+ * @param options - Connection and authentication options
429
+ * @throws {TypeError} If `apiUrl` is not a valid URL
430
+ */
431
+ constructor({ apiUrl, apiPath = "rest/api/latest", agileApiPath = "rest/agile/latest", user, token }) {
432
+ this.listeners = /* @__PURE__ */ new Map();
433
+ this.security = new Security(apiUrl, user, token);
434
+ this.apiPath = apiPath.replace(/^\/|\/$/g, "");
435
+ this.agileApiPath = agileApiPath.replace(/^\/|\/$/g, "");
436
+ }
437
+ /**
438
+ * Subscribes to a client event.
439
+ *
440
+ * @example
441
+ * ```typescript
442
+ * jira.on('request', (event) => {
443
+ * console.log(`${event.method} ${event.url} — ${event.durationMs}ms`);
444
+ * if (event.error) console.error('Request failed:', event.error);
445
+ * });
446
+ * ```
447
+ */
448
+ on(event, callback) {
449
+ const callbacks = this.listeners.get(event) ?? [];
450
+ callbacks.push(callback);
451
+ this.listeners.set(event, callbacks);
452
+ return this;
453
+ }
454
+ emit(event, payload) {
455
+ const callbacks = this.listeners.get(event) ?? [];
456
+ for (const cb of callbacks) {
457
+ cb(payload);
458
+ }
459
+ }
460
+ /**
461
+ * Performs an authenticated GET request to the Jira REST API.
462
+ * @internal
463
+ */
464
+ async request(path, params, options) {
465
+ const apiPath = options?.apiPath ?? this.apiPath;
466
+ const base = `${this.security.getApiUrl()}/${apiPath}${path}`;
467
+ const url = buildUrl(base, params);
468
+ const startedAt = /* @__PURE__ */ new Date();
469
+ let statusCode;
470
+ try {
471
+ const response = await fetch(url, { headers: this.security.getHeaders() });
472
+ statusCode = response.status;
473
+ if (!response.ok) {
474
+ throw new JiraApiError(response.status, response.statusText);
475
+ }
476
+ const data = await response.json();
477
+ this.emit("request", { url, method: "GET", startedAt, finishedAt: /* @__PURE__ */ new Date(), durationMs: Date.now() - startedAt.getTime(), statusCode });
478
+ return data;
479
+ } catch (err) {
480
+ const finishedAt = /* @__PURE__ */ new Date();
481
+ this.emit("request", { url, method: "GET", startedAt, finishedAt, durationMs: finishedAt.getTime() - startedAt.getTime(), statusCode, error: err instanceof Error ? err : new Error(String(err)) });
482
+ throw err;
483
+ }
484
+ }
485
+ async requestPost(path, body, options) {
486
+ const apiPath = options?.apiPath ?? this.apiPath;
487
+ const url = `${this.security.getApiUrl()}/${apiPath}${path}`;
488
+ const startedAt = /* @__PURE__ */ new Date();
489
+ let statusCode;
490
+ try {
491
+ const response = await fetch(url, {
492
+ method: "POST",
493
+ headers: this.security.getHeaders(),
494
+ body: JSON.stringify(body)
495
+ });
496
+ statusCode = response.status;
497
+ if (!response.ok) {
498
+ throw new JiraApiError(response.status, response.statusText);
499
+ }
500
+ const data = await response.json();
501
+ this.emit("request", { url, method: "POST", startedAt, finishedAt: /* @__PURE__ */ new Date(), durationMs: Date.now() - startedAt.getTime(), statusCode });
502
+ return data;
503
+ } catch (err) {
504
+ const finishedAt = /* @__PURE__ */ new Date();
505
+ this.emit("request", { url, method: "POST", startedAt, finishedAt, durationMs: finishedAt.getTime() - startedAt.getTime(), statusCode, error: err instanceof Error ? err : new Error(String(err)) });
506
+ throw err;
507
+ }
508
+ }
509
+ // ─── Issue ───────────────────────────────────────────────────────────────────
510
+ /**
511
+ * Returns an {@link IssueResource} for a given issue key or ID, providing access
512
+ * to issue data and sub-resources (comments, changelog, transitions, etc.).
513
+ *
514
+ * The returned resource can be awaited directly to fetch the issue,
515
+ * or chained to access nested resources.
516
+ *
517
+ * @param issueIdOrKey - The issue key (e.g., `'PROJ-42'`) or numeric ID
518
+ * @returns A chainable issue resource
519
+ *
520
+ * @example
521
+ * ```typescript
522
+ * const issue = await jira.issue('PROJ-42');
523
+ * const comments = await jira.issue('PROJ-42').comments();
524
+ * const changelog = await jira.issue('PROJ-42').changelog();
525
+ * const transitions = await jira.issue('PROJ-42').transitions();
526
+ * ```
527
+ */
528
+ issue(issueIdOrKey) {
529
+ const requestFn = (path, params, options) => this.request(path, params, options);
530
+ return new IssueResource(requestFn, issueIdOrKey);
531
+ }
532
+ // ─── Search ──────────────────────────────────────────────────────────────────
533
+ /**
534
+ * Searches for issues using JQL.
535
+ *
536
+ * `GET /rest/api/latest/search`
537
+ *
538
+ * @param params - Optional: `jql`, `startAt`, `maxResults`, `fields`, `expand`, `validateQuery`
539
+ * @returns A search response containing matching issues
540
+ *
541
+ * @example
542
+ * ```typescript
543
+ * const results = await jira.search({
544
+ * jql: 'project = PROJ AND status = Open ORDER BY created DESC',
545
+ * maxResults: 50,
546
+ * fields: 'summary,status,assignee,priority',
547
+ * });
548
+ * ```
549
+ */
550
+ async search(params) {
551
+ return this.request(
552
+ "/search",
553
+ params
554
+ );
555
+ }
556
+ // ─── Projects ────────────────────────────────────────────────────────────────
557
+ /**
558
+ * Fetches all projects accessible to the authenticated user.
559
+ *
560
+ * `GET /rest/api/latest/project`
561
+ *
562
+ * @param params - Optional: `expand`, `recent`
563
+ * @returns An array of projects
564
+ */
565
+ async projects(params) {
566
+ return this.request(
567
+ "/project",
568
+ params
569
+ );
570
+ }
571
+ /**
572
+ * Returns a {@link ProjectResource} for a given project key or ID.
573
+ *
574
+ * The returned resource can be awaited directly to fetch project info,
575
+ * or chained to access nested resources.
576
+ *
577
+ * @param projectIdOrKey - The project key (e.g., `'PROJ'`) or numeric ID
578
+ * @returns A chainable project resource
579
+ *
580
+ * @example
581
+ * ```typescript
582
+ * const project = await jira.project('PROJ');
583
+ * const components = await jira.project('PROJ').components();
584
+ * const versions = await jira.project('PROJ').versions();
585
+ * const statuses = await jira.project('PROJ').statuses();
586
+ * ```
587
+ */
588
+ project(projectIdOrKey) {
589
+ const requestFn = (path, params, options) => this.request(path, params, options);
590
+ return new ProjectResource(requestFn, projectIdOrKey);
591
+ }
592
+ // ─── Users ───────────────────────────────────────────────────────────────────
593
+ /**
594
+ * Searches for users.
595
+ *
596
+ * `GET /rest/api/latest/user/search`
597
+ *
598
+ * @param params - Optional: `username`, `startAt`, `maxResults`
599
+ * @returns An array of users
600
+ */
601
+ async users(params) {
602
+ return this.request(
603
+ "/user/search",
604
+ params
605
+ );
606
+ }
607
+ /**
608
+ * Fetches a single user by username or key.
609
+ *
610
+ * `GET /rest/api/latest/user`
611
+ *
612
+ * @param username - The username (login name) to look up
613
+ * @returns The user object
614
+ *
615
+ * @example
616
+ * ```typescript
617
+ * const user = await jira.user('pilmee');
618
+ * ```
619
+ */
620
+ async user(username) {
621
+ return this.request("/user", { username });
622
+ }
623
+ /**
624
+ * Fetches the currently authenticated user.
625
+ *
626
+ * `GET /rest/api/latest/myself`
627
+ *
628
+ * @returns The authenticated user object
629
+ *
630
+ * @example
631
+ * ```typescript
632
+ * const me = await jira.currentUser();
633
+ * ```
634
+ */
635
+ async currentUser() {
636
+ return this.request("/myself");
637
+ }
638
+ // ─── Boards (Agile) ──────────────────────────────────────────────────────────
639
+ /**
640
+ * Fetches all boards accessible to the authenticated user.
641
+ *
642
+ * `GET /rest/agile/latest/board`
643
+ *
644
+ * @param params - Optional: `startAt`, `maxResults`, `type`, `name`, `projectKeyOrId`
645
+ * @returns A paged response of boards
646
+ */
647
+ async boards(params) {
648
+ return this.request(
649
+ "/board",
650
+ params,
651
+ { apiPath: this.agileApiPath }
652
+ );
653
+ }
654
+ /**
655
+ * Returns a {@link BoardResource} for a given board ID.
656
+ *
657
+ * The returned resource can be awaited directly to fetch board info,
658
+ * or chained to access nested resources (sprints, issues, backlog).
659
+ *
660
+ * @param boardId - The numeric board ID
661
+ * @returns A chainable board resource
662
+ *
663
+ * @example
664
+ * ```typescript
665
+ * const board = await jira.board(42);
666
+ * const sprints = await jira.board(42).sprints({ state: 'active' });
667
+ * const backlog = await jira.board(42).backlog({ maxResults: 50 });
668
+ * const sprintIssues = await jira.board(42).sprint(10).issues();
669
+ * ```
670
+ */
671
+ board(boardId) {
672
+ const requestFn = (path, params, options) => this.request(path, params, options);
673
+ return new BoardResource(requestFn, this.agileApiPath, boardId);
674
+ }
675
+ // ─── Metadata ────────────────────────────────────────────────────────────────
676
+ /**
677
+ * Fetches all issue types available to the authenticated user.
678
+ *
679
+ * `GET /rest/api/latest/issuetype`
680
+ *
681
+ * @returns An array of issue types
682
+ */
683
+ async issuetypes() {
684
+ return this.request("/issuetype");
685
+ }
686
+ /**
687
+ * Fetches a single issue type by ID.
688
+ *
689
+ * `GET /rest/api/latest/issuetype/{id}`
690
+ *
691
+ * @param id - The issue type ID
692
+ * @returns The issue type object
693
+ */
694
+ async issuetype(id) {
695
+ return this.request(`/issuetype/${id}`);
696
+ }
697
+ /**
698
+ * Fetches all priorities.
699
+ *
700
+ * `GET /rest/api/latest/priority`
701
+ *
702
+ * @returns An array of priorities
703
+ */
704
+ async priorities() {
705
+ return this.request("/priority");
706
+ }
707
+ /**
708
+ * Fetches a single priority by ID.
709
+ *
710
+ * `GET /rest/api/latest/priority/{id}`
711
+ *
712
+ * @param id - The priority ID
713
+ * @returns The priority object
714
+ */
715
+ async priority(id) {
716
+ return this.request(`/priority/${id}`);
717
+ }
718
+ /**
719
+ * Fetches all statuses.
720
+ *
721
+ * `GET /rest/api/latest/status`
722
+ *
723
+ * @returns An array of statuses
724
+ */
725
+ async statuses() {
726
+ return this.request("/status");
727
+ }
728
+ /**
729
+ * Fetches a single status by ID or name.
730
+ *
731
+ * `GET /rest/api/latest/status/{idOrName}`
732
+ *
733
+ * @param idOrName - The status ID or name
734
+ * @returns The status object
735
+ */
736
+ async status(idOrName) {
737
+ return this.request(`/status/${encodeURIComponent(idOrName)}`);
738
+ }
739
+ /**
740
+ * Fetches all issue fields (system and custom).
741
+ *
742
+ * `GET /rest/api/latest/field`
743
+ *
744
+ * @returns An array of fields
745
+ */
746
+ async fields() {
747
+ return this.request("/field");
748
+ }
749
+ /**
750
+ * Fetches all issue link types.
751
+ *
752
+ * `GET /rest/api/latest/issueLinkType`
753
+ *
754
+ * @returns An object with the list of issue link types
755
+ */
756
+ async issueLinkTypes() {
757
+ return this.request("/issueLinkType");
758
+ }
759
+ /**
760
+ * Fetches the authenticated user's favourite filters.
761
+ *
762
+ * `GET /rest/api/latest/filter/favourite`
763
+ *
764
+ * @returns An array of filters
765
+ */
766
+ async favouriteFilters() {
767
+ return this.request("/filter/favourite");
768
+ }
769
+ /**
770
+ * Fetches a single filter by ID.
771
+ *
772
+ * `GET /rest/api/latest/filter/{id}`
773
+ *
774
+ * @param filterId - The numeric filter ID
775
+ * @returns The filter object
776
+ */
777
+ async filter(filterId) {
778
+ return this.request(`/filter/${filterId}`);
779
+ }
780
+ /**
781
+ * Fetches issues using a `POST` search, which supports larger JQL queries
782
+ * and additional options such as specifying the fields list as an array.
783
+ *
784
+ * `POST /rest/api/latest/search`
785
+ *
786
+ * @param body - Search request body
787
+ * @returns A search response containing matching issues
788
+ *
789
+ * @example
790
+ * ```typescript
791
+ * const results = await jira.searchPost({
792
+ * jql: 'project = PROJ AND status = Open',
793
+ * maxResults: 100,
794
+ * fields: ['summary', 'status', 'assignee'],
795
+ * });
796
+ * ```
797
+ */
798
+ async searchPost(body) {
799
+ return this.requestPost("/search", body);
800
+ }
801
+ // ─── Components & Versions ───────────────────────────────────────────────────
802
+ /**
803
+ * Fetches a single component by ID.
804
+ *
805
+ * `GET /rest/api/latest/component/{id}`
806
+ *
807
+ * @param componentId - The component ID
808
+ * @returns The component object
809
+ */
810
+ async component(componentId) {
811
+ return this.request(`/component/${componentId}`);
812
+ }
813
+ /**
814
+ * Fetches a single version by ID.
815
+ *
816
+ * `GET /rest/api/latest/version/{id}`
817
+ *
818
+ * @param versionId - The version ID
819
+ * @returns The version object
820
+ */
821
+ async version(versionId) {
822
+ return this.request(`/version/${versionId}`);
823
+ }
824
+ /**
825
+ * Fetches the related issue counts for a version.
826
+ *
827
+ * `GET /rest/api/latest/version/{id}/relatedIssueCounts`
828
+ *
829
+ * @param versionId - The version ID
830
+ * @returns Issue counts by type
831
+ */
832
+ async versionIssueCounts(versionId) {
833
+ return this.request(`/version/${versionId}/relatedIssueCounts`);
834
+ }
835
+ /**
836
+ * Fetches the number of unresolved issues for a version.
837
+ *
838
+ * `GET /rest/api/latest/version/{id}/unresolvedIssueCount`
839
+ *
840
+ * @param versionId - The version ID
841
+ * @returns The unresolved issue count
842
+ */
843
+ async versionUnresolvedIssueCount(versionId) {
844
+ return this.request(`/version/${versionId}/unresolvedIssueCount`);
845
+ }
846
+ };
847
+ function buildUrl(base, params) {
848
+ if (!params) return base;
849
+ const entries = Object.entries(params).filter(([, v]) => v !== void 0);
850
+ if (entries.length === 0) return base;
851
+ const search = new URLSearchParams(entries.map(([k, v]) => [k, String(v)]));
852
+ return `${base}?${search.toString()}`;
853
+ }
854
+ export {
855
+ BoardResource,
856
+ IssueResource,
857
+ JiraApiError,
858
+ JiraClient,
859
+ ProjectResource,
860
+ Security,
861
+ SprintResource
862
+ };
863
+ //# sourceMappingURL=index.mjs.map