bitbucket-datacenter-api-client 0.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.
- package/LICENSE +21 -0
- package/dist/index.d.mts +455 -0
- package/dist/index.d.ts +455 -0
- package/dist/index.js +272 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +249 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +48 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
BitbucketClient: () => BitbucketClient,
|
|
24
|
+
ProjectResource: () => ProjectResource,
|
|
25
|
+
RepositoryResource: () => RepositoryResource,
|
|
26
|
+
Security: () => Security
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
|
|
30
|
+
// src/security/Security.ts
|
|
31
|
+
function toBase64(value) {
|
|
32
|
+
if (typeof btoa !== "undefined") {
|
|
33
|
+
return btoa(value);
|
|
34
|
+
}
|
|
35
|
+
return Buffer.from(value).toString("base64");
|
|
36
|
+
}
|
|
37
|
+
var Security = class {
|
|
38
|
+
/**
|
|
39
|
+
* Creates a new Security instance with Basic Authentication credentials.
|
|
40
|
+
*
|
|
41
|
+
* @param apiUrl - The base URL of the Bitbucket Data Center instance (e.g., `https://bitbucket.example.com`).
|
|
42
|
+
* Must be a valid URL; throws if it cannot be parsed.
|
|
43
|
+
* @param user - The username to authenticate with
|
|
44
|
+
* @param token - The personal access token or password to authenticate with
|
|
45
|
+
*
|
|
46
|
+
* @throws {TypeError} If `apiUrl` is not a valid URL
|
|
47
|
+
*/
|
|
48
|
+
constructor(apiUrl, user, token) {
|
|
49
|
+
if (!URL.canParse(apiUrl)) {
|
|
50
|
+
throw new TypeError(`Invalid apiUrl: "${apiUrl}" is not a valid URL`);
|
|
51
|
+
}
|
|
52
|
+
this.apiUrl = apiUrl.replace(/\/$/, "");
|
|
53
|
+
this.authorizationHeader = `Basic ${toBase64(`${user}:${token}`)}`;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Returns the base URL of the Bitbucket Data Center instance, without a trailing slash.
|
|
57
|
+
*
|
|
58
|
+
* @returns The API base URL
|
|
59
|
+
*/
|
|
60
|
+
getApiUrl() {
|
|
61
|
+
return this.apiUrl;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Returns the value of the `Authorization` header for Basic Authentication.
|
|
65
|
+
*
|
|
66
|
+
* @returns The Authorization header value in the format `Basic <base64-encoded-credentials>`
|
|
67
|
+
*/
|
|
68
|
+
getAuthorizationHeader() {
|
|
69
|
+
return this.authorizationHeader;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Returns the full set of HTTP headers required for authenticated API requests.
|
|
73
|
+
*
|
|
74
|
+
* @returns An object containing `Authorization`, `Content-Type`, and `Accept` headers
|
|
75
|
+
*/
|
|
76
|
+
getHeaders() {
|
|
77
|
+
return {
|
|
78
|
+
Authorization: this.authorizationHeader,
|
|
79
|
+
"Content-Type": "application/json",
|
|
80
|
+
Accept: "application/json"
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// src/resources/RepositoryResource.ts
|
|
86
|
+
var RepositoryResource = class {
|
|
87
|
+
/** @internal */
|
|
88
|
+
constructor(request, projectKey, repoSlug) {
|
|
89
|
+
this.request = request;
|
|
90
|
+
this.basePath = `/projects/${projectKey}/repos/${repoSlug}`;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Allows the resource to be awaited directly, resolving with the repository info.
|
|
94
|
+
* Delegates to {@link RepositoryResource.get}.
|
|
95
|
+
*/
|
|
96
|
+
then(onfulfilled, onrejected) {
|
|
97
|
+
return this.get().then(onfulfilled, onrejected);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Fetches the repository details.
|
|
101
|
+
*
|
|
102
|
+
* `GET /rest/api/latest/projects/{key}/repos/{slug}`
|
|
103
|
+
*
|
|
104
|
+
* @returns The repository object
|
|
105
|
+
*/
|
|
106
|
+
async get() {
|
|
107
|
+
return this.request(this.basePath);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Fetches pull requests for this repository.
|
|
111
|
+
*
|
|
112
|
+
* `GET /rest/api/latest/projects/{key}/repos/{slug}/pull-requests`
|
|
113
|
+
*
|
|
114
|
+
* @param params - Optional filters: `limit`, `start`, `state`, `direction`, `at`, `order`
|
|
115
|
+
* @returns An array of pull requests
|
|
116
|
+
*/
|
|
117
|
+
async pullRequests(params) {
|
|
118
|
+
const data = await this.request(
|
|
119
|
+
`${this.basePath}/pull-requests`,
|
|
120
|
+
params
|
|
121
|
+
);
|
|
122
|
+
return data.values;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Fetches commits for this repository.
|
|
126
|
+
*
|
|
127
|
+
* `GET /rest/api/latest/projects/{key}/repos/{slug}/commits`
|
|
128
|
+
*
|
|
129
|
+
* @param params - Optional filters: `limit`, `start`, `until`, `since`, `path`, `merges`, `followRenames`, `ignoreMissing`
|
|
130
|
+
* @returns An array of commits
|
|
131
|
+
*/
|
|
132
|
+
async commits(params) {
|
|
133
|
+
const data = await this.request(
|
|
134
|
+
`${this.basePath}/commits`,
|
|
135
|
+
params
|
|
136
|
+
);
|
|
137
|
+
return data.values;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// src/resources/ProjectResource.ts
|
|
142
|
+
var ProjectResource = class {
|
|
143
|
+
/** @internal */
|
|
144
|
+
constructor(request, key) {
|
|
145
|
+
this.request = request;
|
|
146
|
+
this.key = key;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Allows the resource to be awaited directly, resolving with the project info.
|
|
150
|
+
* Delegates to {@link ProjectResource.get}.
|
|
151
|
+
*/
|
|
152
|
+
then(onfulfilled, onrejected) {
|
|
153
|
+
return this.get().then(onfulfilled, onrejected);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Fetches the project details.
|
|
157
|
+
*
|
|
158
|
+
* `GET /rest/api/latest/projects/{key}`
|
|
159
|
+
*
|
|
160
|
+
* @returns The project object
|
|
161
|
+
*/
|
|
162
|
+
async get() {
|
|
163
|
+
return this.request(`/projects/${this.key}`);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Fetches repositories belonging to this project.
|
|
167
|
+
*
|
|
168
|
+
* `GET /rest/api/latest/projects/{key}/repos`
|
|
169
|
+
*
|
|
170
|
+
* @param params - Optional filters: `limit`, `start`, `slug`, `name`, `permission`
|
|
171
|
+
* @returns An array of repositories
|
|
172
|
+
*/
|
|
173
|
+
async repos(params) {
|
|
174
|
+
const data = await this.request(
|
|
175
|
+
`/projects/${this.key}/repos`,
|
|
176
|
+
params
|
|
177
|
+
);
|
|
178
|
+
return data.values;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Returns a {@link RepositoryResource} for a given repository slug, providing
|
|
182
|
+
* access to repository-level data and sub-resources (pull requests, commits, etc.).
|
|
183
|
+
*
|
|
184
|
+
* The returned resource can be awaited directly to fetch repository info,
|
|
185
|
+
* or chained to access nested resources.
|
|
186
|
+
*
|
|
187
|
+
* @param repoSlug - The repository slug (e.g., `'my-repo'`)
|
|
188
|
+
* @returns A chainable repository resource
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* ```typescript
|
|
192
|
+
* const repo = await bbClient.project('PROJ').repo('my-repo');
|
|
193
|
+
* const prs = await bbClient.project('PROJ').repo('my-repo').pullRequests({ state: 'OPEN' });
|
|
194
|
+
* const commits = await bbClient.project('PROJ').repo('my-repo').commits({ limit: 10 });
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
repo(repoSlug) {
|
|
198
|
+
return new RepositoryResource(this.request, this.key, repoSlug);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// src/BitbucketClient.ts
|
|
203
|
+
var BitbucketClient = class {
|
|
204
|
+
/**
|
|
205
|
+
* @param options - Connection and authentication options
|
|
206
|
+
* @throws {TypeError} If `apiUrl` is not a valid URL
|
|
207
|
+
*/
|
|
208
|
+
constructor({ apiUrl, user, token }) {
|
|
209
|
+
this.security = new Security(apiUrl, user, token);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Performs an authenticated GET request to the Bitbucket REST API.
|
|
213
|
+
*
|
|
214
|
+
* @param path - API path relative to `/rest/api/latest` (e.g., `'/projects'`)
|
|
215
|
+
* @param params - Optional query parameters to append to the URL
|
|
216
|
+
* @throws {Error} If the HTTP response is not OK
|
|
217
|
+
* @internal
|
|
218
|
+
*/
|
|
219
|
+
async request(path, params) {
|
|
220
|
+
const base = `${this.security.getApiUrl()}/rest/api/latest${path}`;
|
|
221
|
+
const url = buildUrl(base, params);
|
|
222
|
+
const response = await fetch(url, { headers: this.security.getHeaders() });
|
|
223
|
+
if (!response.ok) {
|
|
224
|
+
throw new Error(`Bitbucket API error: ${response.status} ${response.statusText}`);
|
|
225
|
+
}
|
|
226
|
+
return response.json();
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Fetches all projects accessible to the authenticated user.
|
|
230
|
+
*
|
|
231
|
+
* `GET /rest/api/latest/projects`
|
|
232
|
+
*
|
|
233
|
+
* @param params - Optional filters: `limit`, `start`, `name`, `permission`
|
|
234
|
+
* @returns An array of projects
|
|
235
|
+
*/
|
|
236
|
+
async projects(params) {
|
|
237
|
+
const data = await this.request(
|
|
238
|
+
"/projects",
|
|
239
|
+
params
|
|
240
|
+
);
|
|
241
|
+
return data.values;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Returns a {@link ProjectResource} for a given project key, providing access
|
|
245
|
+
* to project-level data and sub-resources.
|
|
246
|
+
*
|
|
247
|
+
* The returned resource can be awaited directly to fetch project info,
|
|
248
|
+
* or chained to access nested resources.
|
|
249
|
+
*
|
|
250
|
+
* @param projectKey - The project key (e.g., `'PROJ'`)
|
|
251
|
+
* @returns A chainable project resource
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```typescript
|
|
255
|
+
* const project = await bbClient.project('PROJ');
|
|
256
|
+
* const repos = await bbClient.project('PROJ').repos({ limit: 10 });
|
|
257
|
+
* const prs = await bbClient.project('PROJ').repo('my-repo').pullRequests();
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
project(projectKey) {
|
|
261
|
+
const request = (path, params) => this.request(path, params);
|
|
262
|
+
return new ProjectResource(request, projectKey);
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
function buildUrl(base, params) {
|
|
266
|
+
if (!params) return base;
|
|
267
|
+
const entries = Object.entries(params).filter(([, v]) => v !== void 0);
|
|
268
|
+
if (entries.length === 0) return base;
|
|
269
|
+
const search = new URLSearchParams(entries.map(([k, v]) => [k, String(v)]));
|
|
270
|
+
return `${base}?${search.toString()}`;
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/security/Security.ts","../src/resources/RepositoryResource.ts","../src/resources/ProjectResource.ts","../src/BitbucketClient.ts"],"sourcesContent":["export { BitbucketClient } from './BitbucketClient';\nexport type { BitbucketClientOptions } from './BitbucketClient';\nexport { Security } from './security/Security';\nexport { ProjectResource } from './resources/ProjectResource';\nexport { RepositoryResource } from './resources/RepositoryResource';\nexport type { BitbucketProject, ProjectsParams } from './domain/Project';\nexport type { BitbucketRepository, ReposParams } from './domain/Repository';\nexport type { BitbucketPullRequest, BitbucketParticipant, BitbucketRef, PullRequestsParams } from './domain/PullRequest';\nexport type { BitbucketCommit, BitbucketCommitAuthor, CommitsParams } from './domain/Commit';\nexport type { PaginationParams, PagedResponse } from './domain/Pagination';\n","/**\n * Encodes a string to Base64 in a way that works in both Node.js and browsers.\n * @internal\n */\nfunction toBase64(value: string): string {\n if (typeof btoa !== 'undefined') {\n return btoa(value);\n }\n return Buffer.from(value).toString('base64');\n}\n\n/**\n * Handles Basic Authentication for Bitbucket Data Center REST API requests.\n *\n * @example\n * ```typescript\n * const security = new Security(\n * 'https://bitbucket.example.com',\n * 'my-user',\n * 'my-token'\n * );\n *\n * const headers = security.getHeaders();\n * // { Authorization: 'Basic <base64>', 'Content-Type': 'application/json', Accept: 'application/json' }\n * ```\n */\nexport class Security {\n private readonly apiUrl: string;\n private readonly authorizationHeader: string;\n\n /**\n * Creates a new Security instance with Basic Authentication credentials.\n *\n * @param apiUrl - The base URL of the Bitbucket Data Center instance (e.g., `https://bitbucket.example.com`).\n * Must be a valid URL; throws if it cannot be parsed.\n * @param user - The username to authenticate with\n * @param token - The personal access token or password to authenticate with\n *\n * @throws {TypeError} If `apiUrl` is not a valid URL\n */\n constructor(apiUrl: string, user: string, token: string) {\n if (!URL.canParse(apiUrl)) {\n throw new TypeError(`Invalid apiUrl: \"${apiUrl}\" is not a valid URL`);\n }\n this.apiUrl = apiUrl.replace(/\\/$/, '');\n this.authorizationHeader = `Basic ${toBase64(`${user}:${token}`)}`;\n }\n\n /**\n * Returns the base URL of the Bitbucket Data Center instance, without a trailing slash.\n *\n * @returns The API base URL\n */\n getApiUrl(): string {\n return this.apiUrl;\n }\n\n /**\n * Returns the value of the `Authorization` header for Basic Authentication.\n *\n * @returns The Authorization header value in the format `Basic <base64-encoded-credentials>`\n */\n getAuthorizationHeader(): string {\n return this.authorizationHeader;\n }\n\n /**\n * Returns the full set of HTTP headers required for authenticated API requests.\n *\n * @returns An object containing `Authorization`, `Content-Type`, and `Accept` headers\n */\n getHeaders(): Record<string, string> {\n return {\n Authorization: this.authorizationHeader,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n };\n }\n}\n","import type { BitbucketRepository } from '../domain/Repository';\nimport type { BitbucketPullRequest, PullRequestsParams } from '../domain/PullRequest';\nimport type { BitbucketCommit, CommitsParams } from '../domain/Commit';\nimport type { PagedResponse } from '../domain/Pagination';\nimport type { RequestFn } from './ProjectResource';\n\n/**\n * Represents a Bitbucket repository resource with chainable async methods.\n *\n * Implements `PromiseLike<BitbucketRepository>` so it can be awaited directly\n * to fetch repository info, while also exposing sub-resource methods.\n *\n * @example\n * ```typescript\n * // Await directly to get repository info\n * const repo = await bbClient.project('PROJ').repo('my-repo');\n *\n * // Get pull requests\n * const prs = await bbClient.project('PROJ').repo('my-repo').pullRequests({ state: 'OPEN' });\n *\n * // Get commits\n * const commits = await bbClient.project('PROJ').repo('my-repo').commits({ limit: 10 });\n * ```\n */\nexport class RepositoryResource implements PromiseLike<BitbucketRepository> {\n private readonly basePath: string;\n\n /** @internal */\n constructor(\n private readonly request: RequestFn,\n projectKey: string,\n repoSlug: string,\n ) {\n this.basePath = `/projects/${projectKey}/repos/${repoSlug}`;\n }\n\n /**\n * Allows the resource to be awaited directly, resolving with the repository info.\n * Delegates to {@link RepositoryResource.get}.\n */\n then<TResult1 = BitbucketRepository, TResult2 = never>(\n onfulfilled?: ((value: BitbucketRepository) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): PromiseLike<TResult1 | TResult2> {\n return this.get().then(onfulfilled, onrejected);\n }\n\n /**\n * Fetches the repository details.\n *\n * `GET /rest/api/latest/projects/{key}/repos/{slug}`\n *\n * @returns The repository object\n */\n async get(): Promise<BitbucketRepository> {\n return this.request<BitbucketRepository>(this.basePath);\n }\n\n /**\n * Fetches pull requests for this repository.\n *\n * `GET /rest/api/latest/projects/{key}/repos/{slug}/pull-requests`\n *\n * @param params - Optional filters: `limit`, `start`, `state`, `direction`, `at`, `order`\n * @returns An array of pull requests\n */\n async pullRequests(params?: PullRequestsParams): Promise<BitbucketPullRequest[]> {\n const data = await this.request<PagedResponse<BitbucketPullRequest>>(\n `${this.basePath}/pull-requests`,\n params as Record<string, string | number | boolean>,\n );\n return data.values;\n }\n\n /**\n * Fetches commits for this repository.\n *\n * `GET /rest/api/latest/projects/{key}/repos/{slug}/commits`\n *\n * @param params - Optional filters: `limit`, `start`, `until`, `since`, `path`, `merges`, `followRenames`, `ignoreMissing`\n * @returns An array of commits\n */\n async commits(params?: CommitsParams): Promise<BitbucketCommit[]> {\n const data = await this.request<PagedResponse<BitbucketCommit>>(\n `${this.basePath}/commits`,\n params as Record<string, string | number | boolean>,\n );\n return data.values;\n }\n}\n","import type { BitbucketProject } from '../domain/Project';\nimport type { BitbucketRepository, ReposParams } from '../domain/Repository';\nimport type { PagedResponse } from '../domain/Pagination';\nimport { RepositoryResource } from './RepositoryResource';\n\n/** @internal */\nexport type RequestFn = <T>(\n path: string,\n params?: Record<string, string | number | boolean>,\n) => Promise<T>;\n\n/**\n * Represents a Bitbucket project resource with chainable async methods.\n *\n * Implements `PromiseLike<BitbucketProject>` so it can be awaited directly\n * to fetch the project info, while also exposing sub-resource methods.\n *\n * @example\n * ```typescript\n * // Await directly to get project info\n * const project = await bbClient.project('PROJ');\n *\n * // Get repositories with filters\n * const repos = await bbClient.project('PROJ').repos({ limit: 50, name: 'api' });\n *\n * // Navigate into a specific repository\n * const prs = await bbClient.project('PROJ').repo('my-repo').pullRequests();\n * ```\n */\nexport class ProjectResource implements PromiseLike<BitbucketProject> {\n /** @internal */\n constructor(\n private readonly request: RequestFn,\n private readonly key: string,\n ) {}\n\n /**\n * Allows the resource to be awaited directly, resolving with the project info.\n * Delegates to {@link ProjectResource.get}.\n */\n then<TResult1 = BitbucketProject, TResult2 = never>(\n onfulfilled?: ((value: BitbucketProject) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): PromiseLike<TResult1 | TResult2> {\n return this.get().then(onfulfilled, onrejected);\n }\n\n /**\n * Fetches the project details.\n *\n * `GET /rest/api/latest/projects/{key}`\n *\n * @returns The project object\n */\n async get(): Promise<BitbucketProject> {\n return this.request<BitbucketProject>(`/projects/${this.key}`);\n }\n\n /**\n * Fetches repositories belonging to this project.\n *\n * `GET /rest/api/latest/projects/{key}/repos`\n *\n * @param params - Optional filters: `limit`, `start`, `slug`, `name`, `permission`\n * @returns An array of repositories\n */\n async repos(params?: ReposParams): Promise<BitbucketRepository[]> {\n const data = await this.request<PagedResponse<BitbucketRepository>>(\n `/projects/${this.key}/repos`,\n params as Record<string, string | number | boolean>,\n );\n return data.values;\n }\n\n /**\n * Returns a {@link RepositoryResource} for a given repository slug, providing\n * access to repository-level data and sub-resources (pull requests, commits, etc.).\n *\n * The returned resource can be awaited directly to fetch repository info,\n * or chained to access nested resources.\n *\n * @param repoSlug - The repository slug (e.g., `'my-repo'`)\n * @returns A chainable repository resource\n *\n * @example\n * ```typescript\n * const repo = await bbClient.project('PROJ').repo('my-repo');\n * const prs = await bbClient.project('PROJ').repo('my-repo').pullRequests({ state: 'OPEN' });\n * const commits = await bbClient.project('PROJ').repo('my-repo').commits({ limit: 10 });\n * ```\n */\n repo(repoSlug: string): RepositoryResource {\n return new RepositoryResource(this.request, this.key, repoSlug);\n }\n}\n","import { Security } from './security/Security';\nimport { ProjectResource, type RequestFn } from './resources/ProjectResource';\nimport type { BitbucketProject, ProjectsParams } from './domain/Project';\nimport type { PagedResponse } from './domain/Pagination';\n\n/**\n * Constructor options for {@link BitbucketClient}.\n */\nexport interface BitbucketClientOptions {\n /** The base URL of the Bitbucket Data Center instance (e.g., `https://bitbucket.example.com`) */\n apiUrl: string;\n /** The username to authenticate with */\n user: string;\n /** The personal access token or password to authenticate with */\n token: string;\n}\n\n/**\n * Main entry point for the Bitbucket Data Center REST API client.\n *\n * @example\n * ```typescript\n * const bbClient = new BitbucketClient({\n * apiUrl: 'https://bitbucket.example.com',\n * user: 'john.doe',\n * token: 'my-token',\n * });\n *\n * const projects = await bbClient.projects({ limit: 50 });\n * const project = await bbClient.project('PROJ');\n * const repos = await bbClient.project('PROJ').repos({ name: 'api' });\n * const repo = await bbClient.project('PROJ').repo('my-repo');\n * const prs = await bbClient.project('PROJ').repo('my-repo').pullRequests({ state: 'OPEN' });\n * const commits = await bbClient.project('PROJ').repo('my-repo').commits({ limit: 10 });\n * ```\n */\nexport class BitbucketClient {\n private readonly security: Security;\n\n /**\n * @param options - Connection and authentication options\n * @throws {TypeError} If `apiUrl` is not a valid URL\n */\n constructor({ apiUrl, user, token }: BitbucketClientOptions) {\n this.security = new Security(apiUrl, user, token);\n }\n\n /**\n * Performs an authenticated GET request to the Bitbucket REST API.\n *\n * @param path - API path relative to `/rest/api/latest` (e.g., `'/projects'`)\n * @param params - Optional query parameters to append to the URL\n * @throws {Error} If the HTTP response is not OK\n * @internal\n */\n private async request<T>(\n path: string,\n params?: Record<string, string | number | boolean>,\n ): Promise<T> {\n const base = `${this.security.getApiUrl()}/rest/api/latest${path}`;\n const url = buildUrl(base, params);\n const response = await fetch(url, { headers: this.security.getHeaders() });\n if (!response.ok) {\n throw new Error(`Bitbucket API error: ${response.status} ${response.statusText}`);\n }\n return response.json() as Promise<T>;\n }\n\n /**\n * Fetches all projects accessible to the authenticated user.\n *\n * `GET /rest/api/latest/projects`\n *\n * @param params - Optional filters: `limit`, `start`, `name`, `permission`\n * @returns An array of projects\n */\n async projects(params?: ProjectsParams): Promise<BitbucketProject[]> {\n const data = await this.request<PagedResponse<BitbucketProject>>(\n '/projects',\n params as Record<string, string | number | boolean>,\n );\n return data.values;\n }\n\n /**\n * Returns a {@link ProjectResource} for a given project key, providing access\n * to project-level data and sub-resources.\n *\n * The returned resource can be awaited directly to fetch project info,\n * or chained to access nested resources.\n *\n * @param projectKey - The project key (e.g., `'PROJ'`)\n * @returns A chainable project resource\n *\n * @example\n * ```typescript\n * const project = await bbClient.project('PROJ');\n * const repos = await bbClient.project('PROJ').repos({ limit: 10 });\n * const prs = await bbClient.project('PROJ').repo('my-repo').pullRequests();\n * ```\n */\n project(projectKey: string): ProjectResource {\n const request: RequestFn = <T>(\n path: string,\n params?: Record<string, string | number | boolean>,\n ) => this.request<T>(path, params);\n return new ProjectResource(request, projectKey);\n }\n}\n\n/**\n * Appends query parameters to a URL string, skipping `undefined` values.\n * @internal\n */\nfunction buildUrl(base: string, params?: Record<string, string | number | boolean>): string {\n if (!params) return base;\n const entries = Object.entries(params).filter(([, v]) => v !== undefined);\n if (entries.length === 0) return base;\n const search = new URLSearchParams(entries.map(([k, v]) => [k, String(v)]));\n return `${base}?${search.toString()}`;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
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 Bitbucket Data Center instance (e.g., `https://bitbucket.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 Bitbucket 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/resources/RepositoryResource.ts
|
|
57
|
+
var RepositoryResource = class {
|
|
58
|
+
/** @internal */
|
|
59
|
+
constructor(request, projectKey, repoSlug) {
|
|
60
|
+
this.request = request;
|
|
61
|
+
this.basePath = `/projects/${projectKey}/repos/${repoSlug}`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Allows the resource to be awaited directly, resolving with the repository info.
|
|
65
|
+
* Delegates to {@link RepositoryResource.get}.
|
|
66
|
+
*/
|
|
67
|
+
then(onfulfilled, onrejected) {
|
|
68
|
+
return this.get().then(onfulfilled, onrejected);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Fetches the repository details.
|
|
72
|
+
*
|
|
73
|
+
* `GET /rest/api/latest/projects/{key}/repos/{slug}`
|
|
74
|
+
*
|
|
75
|
+
* @returns The repository object
|
|
76
|
+
*/
|
|
77
|
+
async get() {
|
|
78
|
+
return this.request(this.basePath);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Fetches pull requests for this repository.
|
|
82
|
+
*
|
|
83
|
+
* `GET /rest/api/latest/projects/{key}/repos/{slug}/pull-requests`
|
|
84
|
+
*
|
|
85
|
+
* @param params - Optional filters: `limit`, `start`, `state`, `direction`, `at`, `order`
|
|
86
|
+
* @returns An array of pull requests
|
|
87
|
+
*/
|
|
88
|
+
async pullRequests(params) {
|
|
89
|
+
const data = await this.request(
|
|
90
|
+
`${this.basePath}/pull-requests`,
|
|
91
|
+
params
|
|
92
|
+
);
|
|
93
|
+
return data.values;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Fetches commits for this repository.
|
|
97
|
+
*
|
|
98
|
+
* `GET /rest/api/latest/projects/{key}/repos/{slug}/commits`
|
|
99
|
+
*
|
|
100
|
+
* @param params - Optional filters: `limit`, `start`, `until`, `since`, `path`, `merges`, `followRenames`, `ignoreMissing`
|
|
101
|
+
* @returns An array of commits
|
|
102
|
+
*/
|
|
103
|
+
async commits(params) {
|
|
104
|
+
const data = await this.request(
|
|
105
|
+
`${this.basePath}/commits`,
|
|
106
|
+
params
|
|
107
|
+
);
|
|
108
|
+
return data.values;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// src/resources/ProjectResource.ts
|
|
113
|
+
var ProjectResource = class {
|
|
114
|
+
/** @internal */
|
|
115
|
+
constructor(request, key) {
|
|
116
|
+
this.request = request;
|
|
117
|
+
this.key = key;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Allows the resource to be awaited directly, resolving with the project info.
|
|
121
|
+
* Delegates to {@link ProjectResource.get}.
|
|
122
|
+
*/
|
|
123
|
+
then(onfulfilled, onrejected) {
|
|
124
|
+
return this.get().then(onfulfilled, onrejected);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Fetches the project details.
|
|
128
|
+
*
|
|
129
|
+
* `GET /rest/api/latest/projects/{key}`
|
|
130
|
+
*
|
|
131
|
+
* @returns The project object
|
|
132
|
+
*/
|
|
133
|
+
async get() {
|
|
134
|
+
return this.request(`/projects/${this.key}`);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Fetches repositories belonging to this project.
|
|
138
|
+
*
|
|
139
|
+
* `GET /rest/api/latest/projects/{key}/repos`
|
|
140
|
+
*
|
|
141
|
+
* @param params - Optional filters: `limit`, `start`, `slug`, `name`, `permission`
|
|
142
|
+
* @returns An array of repositories
|
|
143
|
+
*/
|
|
144
|
+
async repos(params) {
|
|
145
|
+
const data = await this.request(
|
|
146
|
+
`/projects/${this.key}/repos`,
|
|
147
|
+
params
|
|
148
|
+
);
|
|
149
|
+
return data.values;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Returns a {@link RepositoryResource} for a given repository slug, providing
|
|
153
|
+
* access to repository-level data and sub-resources (pull requests, commits, etc.).
|
|
154
|
+
*
|
|
155
|
+
* The returned resource can be awaited directly to fetch repository info,
|
|
156
|
+
* or chained to access nested resources.
|
|
157
|
+
*
|
|
158
|
+
* @param repoSlug - The repository slug (e.g., `'my-repo'`)
|
|
159
|
+
* @returns A chainable repository resource
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```typescript
|
|
163
|
+
* const repo = await bbClient.project('PROJ').repo('my-repo');
|
|
164
|
+
* const prs = await bbClient.project('PROJ').repo('my-repo').pullRequests({ state: 'OPEN' });
|
|
165
|
+
* const commits = await bbClient.project('PROJ').repo('my-repo').commits({ limit: 10 });
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
repo(repoSlug) {
|
|
169
|
+
return new RepositoryResource(this.request, this.key, repoSlug);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// src/BitbucketClient.ts
|
|
174
|
+
var BitbucketClient = class {
|
|
175
|
+
/**
|
|
176
|
+
* @param options - Connection and authentication options
|
|
177
|
+
* @throws {TypeError} If `apiUrl` is not a valid URL
|
|
178
|
+
*/
|
|
179
|
+
constructor({ apiUrl, user, token }) {
|
|
180
|
+
this.security = new Security(apiUrl, user, token);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Performs an authenticated GET request to the Bitbucket REST API.
|
|
184
|
+
*
|
|
185
|
+
* @param path - API path relative to `/rest/api/latest` (e.g., `'/projects'`)
|
|
186
|
+
* @param params - Optional query parameters to append to the URL
|
|
187
|
+
* @throws {Error} If the HTTP response is not OK
|
|
188
|
+
* @internal
|
|
189
|
+
*/
|
|
190
|
+
async request(path, params) {
|
|
191
|
+
const base = `${this.security.getApiUrl()}/rest/api/latest${path}`;
|
|
192
|
+
const url = buildUrl(base, params);
|
|
193
|
+
const response = await fetch(url, { headers: this.security.getHeaders() });
|
|
194
|
+
if (!response.ok) {
|
|
195
|
+
throw new Error(`Bitbucket API error: ${response.status} ${response.statusText}`);
|
|
196
|
+
}
|
|
197
|
+
return response.json();
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Fetches all projects accessible to the authenticated user.
|
|
201
|
+
*
|
|
202
|
+
* `GET /rest/api/latest/projects`
|
|
203
|
+
*
|
|
204
|
+
* @param params - Optional filters: `limit`, `start`, `name`, `permission`
|
|
205
|
+
* @returns An array of projects
|
|
206
|
+
*/
|
|
207
|
+
async projects(params) {
|
|
208
|
+
const data = await this.request(
|
|
209
|
+
"/projects",
|
|
210
|
+
params
|
|
211
|
+
);
|
|
212
|
+
return data.values;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Returns a {@link ProjectResource} for a given project key, providing access
|
|
216
|
+
* to project-level data and sub-resources.
|
|
217
|
+
*
|
|
218
|
+
* The returned resource can be awaited directly to fetch project info,
|
|
219
|
+
* or chained to access nested resources.
|
|
220
|
+
*
|
|
221
|
+
* @param projectKey - The project key (e.g., `'PROJ'`)
|
|
222
|
+
* @returns A chainable project resource
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```typescript
|
|
226
|
+
* const project = await bbClient.project('PROJ');
|
|
227
|
+
* const repos = await bbClient.project('PROJ').repos({ limit: 10 });
|
|
228
|
+
* const prs = await bbClient.project('PROJ').repo('my-repo').pullRequests();
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
project(projectKey) {
|
|
232
|
+
const request = (path, params) => this.request(path, params);
|
|
233
|
+
return new ProjectResource(request, projectKey);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
function buildUrl(base, params) {
|
|
237
|
+
if (!params) return base;
|
|
238
|
+
const entries = Object.entries(params).filter(([, v]) => v !== void 0);
|
|
239
|
+
if (entries.length === 0) return base;
|
|
240
|
+
const search = new URLSearchParams(entries.map(([k, v]) => [k, String(v)]));
|
|
241
|
+
return `${base}?${search.toString()}`;
|
|
242
|
+
}
|
|
243
|
+
export {
|
|
244
|
+
BitbucketClient,
|
|
245
|
+
ProjectResource,
|
|
246
|
+
RepositoryResource,
|
|
247
|
+
Security
|
|
248
|
+
};
|
|
249
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/security/Security.ts","../src/resources/RepositoryResource.ts","../src/resources/ProjectResource.ts","../src/BitbucketClient.ts"],"sourcesContent":["/**\n * Encodes a string to Base64 in a way that works in both Node.js and browsers.\n * @internal\n */\nfunction toBase64(value: string): string {\n if (typeof btoa !== 'undefined') {\n return btoa(value);\n }\n return Buffer.from(value).toString('base64');\n}\n\n/**\n * Handles Basic Authentication for Bitbucket Data Center REST API requests.\n *\n * @example\n * ```typescript\n * const security = new Security(\n * 'https://bitbucket.example.com',\n * 'my-user',\n * 'my-token'\n * );\n *\n * const headers = security.getHeaders();\n * // { Authorization: 'Basic <base64>', 'Content-Type': 'application/json', Accept: 'application/json' }\n * ```\n */\nexport class Security {\n private readonly apiUrl: string;\n private readonly authorizationHeader: string;\n\n /**\n * Creates a new Security instance with Basic Authentication credentials.\n *\n * @param apiUrl - The base URL of the Bitbucket Data Center instance (e.g., `https://bitbucket.example.com`).\n * Must be a valid URL; throws if it cannot be parsed.\n * @param user - The username to authenticate with\n * @param token - The personal access token or password to authenticate with\n *\n * @throws {TypeError} If `apiUrl` is not a valid URL\n */\n constructor(apiUrl: string, user: string, token: string) {\n if (!URL.canParse(apiUrl)) {\n throw new TypeError(`Invalid apiUrl: \"${apiUrl}\" is not a valid URL`);\n }\n this.apiUrl = apiUrl.replace(/\\/$/, '');\n this.authorizationHeader = `Basic ${toBase64(`${user}:${token}`)}`;\n }\n\n /**\n * Returns the base URL of the Bitbucket Data Center instance, without a trailing slash.\n *\n * @returns The API base URL\n */\n getApiUrl(): string {\n return this.apiUrl;\n }\n\n /**\n * Returns the value of the `Authorization` header for Basic Authentication.\n *\n * @returns The Authorization header value in the format `Basic <base64-encoded-credentials>`\n */\n getAuthorizationHeader(): string {\n return this.authorizationHeader;\n }\n\n /**\n * Returns the full set of HTTP headers required for authenticated API requests.\n *\n * @returns An object containing `Authorization`, `Content-Type`, and `Accept` headers\n */\n getHeaders(): Record<string, string> {\n return {\n Authorization: this.authorizationHeader,\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n };\n }\n}\n","import type { BitbucketRepository } from '../domain/Repository';\nimport type { BitbucketPullRequest, PullRequestsParams } from '../domain/PullRequest';\nimport type { BitbucketCommit, CommitsParams } from '../domain/Commit';\nimport type { PagedResponse } from '../domain/Pagination';\nimport type { RequestFn } from './ProjectResource';\n\n/**\n * Represents a Bitbucket repository resource with chainable async methods.\n *\n * Implements `PromiseLike<BitbucketRepository>` so it can be awaited directly\n * to fetch repository info, while also exposing sub-resource methods.\n *\n * @example\n * ```typescript\n * // Await directly to get repository info\n * const repo = await bbClient.project('PROJ').repo('my-repo');\n *\n * // Get pull requests\n * const prs = await bbClient.project('PROJ').repo('my-repo').pullRequests({ state: 'OPEN' });\n *\n * // Get commits\n * const commits = await bbClient.project('PROJ').repo('my-repo').commits({ limit: 10 });\n * ```\n */\nexport class RepositoryResource implements PromiseLike<BitbucketRepository> {\n private readonly basePath: string;\n\n /** @internal */\n constructor(\n private readonly request: RequestFn,\n projectKey: string,\n repoSlug: string,\n ) {\n this.basePath = `/projects/${projectKey}/repos/${repoSlug}`;\n }\n\n /**\n * Allows the resource to be awaited directly, resolving with the repository info.\n * Delegates to {@link RepositoryResource.get}.\n */\n then<TResult1 = BitbucketRepository, TResult2 = never>(\n onfulfilled?: ((value: BitbucketRepository) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): PromiseLike<TResult1 | TResult2> {\n return this.get().then(onfulfilled, onrejected);\n }\n\n /**\n * Fetches the repository details.\n *\n * `GET /rest/api/latest/projects/{key}/repos/{slug}`\n *\n * @returns The repository object\n */\n async get(): Promise<BitbucketRepository> {\n return this.request<BitbucketRepository>(this.basePath);\n }\n\n /**\n * Fetches pull requests for this repository.\n *\n * `GET /rest/api/latest/projects/{key}/repos/{slug}/pull-requests`\n *\n * @param params - Optional filters: `limit`, `start`, `state`, `direction`, `at`, `order`\n * @returns An array of pull requests\n */\n async pullRequests(params?: PullRequestsParams): Promise<BitbucketPullRequest[]> {\n const data = await this.request<PagedResponse<BitbucketPullRequest>>(\n `${this.basePath}/pull-requests`,\n params as Record<string, string | number | boolean>,\n );\n return data.values;\n }\n\n /**\n * Fetches commits for this repository.\n *\n * `GET /rest/api/latest/projects/{key}/repos/{slug}/commits`\n *\n * @param params - Optional filters: `limit`, `start`, `until`, `since`, `path`, `merges`, `followRenames`, `ignoreMissing`\n * @returns An array of commits\n */\n async commits(params?: CommitsParams): Promise<BitbucketCommit[]> {\n const data = await this.request<PagedResponse<BitbucketCommit>>(\n `${this.basePath}/commits`,\n params as Record<string, string | number | boolean>,\n );\n return data.values;\n }\n}\n","import type { BitbucketProject } from '../domain/Project';\nimport type { BitbucketRepository, ReposParams } from '../domain/Repository';\nimport type { PagedResponse } from '../domain/Pagination';\nimport { RepositoryResource } from './RepositoryResource';\n\n/** @internal */\nexport type RequestFn = <T>(\n path: string,\n params?: Record<string, string | number | boolean>,\n) => Promise<T>;\n\n/**\n * Represents a Bitbucket project resource with chainable async methods.\n *\n * Implements `PromiseLike<BitbucketProject>` so it can be awaited directly\n * to fetch the project info, while also exposing sub-resource methods.\n *\n * @example\n * ```typescript\n * // Await directly to get project info\n * const project = await bbClient.project('PROJ');\n *\n * // Get repositories with filters\n * const repos = await bbClient.project('PROJ').repos({ limit: 50, name: 'api' });\n *\n * // Navigate into a specific repository\n * const prs = await bbClient.project('PROJ').repo('my-repo').pullRequests();\n * ```\n */\nexport class ProjectResource implements PromiseLike<BitbucketProject> {\n /** @internal */\n constructor(\n private readonly request: RequestFn,\n private readonly key: string,\n ) {}\n\n /**\n * Allows the resource to be awaited directly, resolving with the project info.\n * Delegates to {@link ProjectResource.get}.\n */\n then<TResult1 = BitbucketProject, TResult2 = never>(\n onfulfilled?: ((value: BitbucketProject) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,\n ): PromiseLike<TResult1 | TResult2> {\n return this.get().then(onfulfilled, onrejected);\n }\n\n /**\n * Fetches the project details.\n *\n * `GET /rest/api/latest/projects/{key}`\n *\n * @returns The project object\n */\n async get(): Promise<BitbucketProject> {\n return this.request<BitbucketProject>(`/projects/${this.key}`);\n }\n\n /**\n * Fetches repositories belonging to this project.\n *\n * `GET /rest/api/latest/projects/{key}/repos`\n *\n * @param params - Optional filters: `limit`, `start`, `slug`, `name`, `permission`\n * @returns An array of repositories\n */\n async repos(params?: ReposParams): Promise<BitbucketRepository[]> {\n const data = await this.request<PagedResponse<BitbucketRepository>>(\n `/projects/${this.key}/repos`,\n params as Record<string, string | number | boolean>,\n );\n return data.values;\n }\n\n /**\n * Returns a {@link RepositoryResource} for a given repository slug, providing\n * access to repository-level data and sub-resources (pull requests, commits, etc.).\n *\n * The returned resource can be awaited directly to fetch repository info,\n * or chained to access nested resources.\n *\n * @param repoSlug - The repository slug (e.g., `'my-repo'`)\n * @returns A chainable repository resource\n *\n * @example\n * ```typescript\n * const repo = await bbClient.project('PROJ').repo('my-repo');\n * const prs = await bbClient.project('PROJ').repo('my-repo').pullRequests({ state: 'OPEN' });\n * const commits = await bbClient.project('PROJ').repo('my-repo').commits({ limit: 10 });\n * ```\n */\n repo(repoSlug: string): RepositoryResource {\n return new RepositoryResource(this.request, this.key, repoSlug);\n }\n}\n","import { Security } from './security/Security';\nimport { ProjectResource, type RequestFn } from './resources/ProjectResource';\nimport type { BitbucketProject, ProjectsParams } from './domain/Project';\nimport type { PagedResponse } from './domain/Pagination';\n\n/**\n * Constructor options for {@link BitbucketClient}.\n */\nexport interface BitbucketClientOptions {\n /** The base URL of the Bitbucket Data Center instance (e.g., `https://bitbucket.example.com`) */\n apiUrl: string;\n /** The username to authenticate with */\n user: string;\n /** The personal access token or password to authenticate with */\n token: string;\n}\n\n/**\n * Main entry point for the Bitbucket Data Center REST API client.\n *\n * @example\n * ```typescript\n * const bbClient = new BitbucketClient({\n * apiUrl: 'https://bitbucket.example.com',\n * user: 'john.doe',\n * token: 'my-token',\n * });\n *\n * const projects = await bbClient.projects({ limit: 50 });\n * const project = await bbClient.project('PROJ');\n * const repos = await bbClient.project('PROJ').repos({ name: 'api' });\n * const repo = await bbClient.project('PROJ').repo('my-repo');\n * const prs = await bbClient.project('PROJ').repo('my-repo').pullRequests({ state: 'OPEN' });\n * const commits = await bbClient.project('PROJ').repo('my-repo').commits({ limit: 10 });\n * ```\n */\nexport class BitbucketClient {\n private readonly security: Security;\n\n /**\n * @param options - Connection and authentication options\n * @throws {TypeError} If `apiUrl` is not a valid URL\n */\n constructor({ apiUrl, user, token }: BitbucketClientOptions) {\n this.security = new Security(apiUrl, user, token);\n }\n\n /**\n * Performs an authenticated GET request to the Bitbucket REST API.\n *\n * @param path - API path relative to `/rest/api/latest` (e.g., `'/projects'`)\n * @param params - Optional query parameters to append to the URL\n * @throws {Error} If the HTTP response is not OK\n * @internal\n */\n private async request<T>(\n path: string,\n params?: Record<string, string | number | boolean>,\n ): Promise<T> {\n const base = `${this.security.getApiUrl()}/rest/api/latest${path}`;\n const url = buildUrl(base, params);\n const response = await fetch(url, { headers: this.security.getHeaders() });\n if (!response.ok) {\n throw new Error(`Bitbucket API error: ${response.status} ${response.statusText}`);\n }\n return response.json() as Promise<T>;\n }\n\n /**\n * Fetches all projects accessible to the authenticated user.\n *\n * `GET /rest/api/latest/projects`\n *\n * @param params - Optional filters: `limit`, `start`, `name`, `permission`\n * @returns An array of projects\n */\n async projects(params?: ProjectsParams): Promise<BitbucketProject[]> {\n const data = await this.request<PagedResponse<BitbucketProject>>(\n '/projects',\n params as Record<string, string | number | boolean>,\n );\n return data.values;\n }\n\n /**\n * Returns a {@link ProjectResource} for a given project key, providing access\n * to project-level data and sub-resources.\n *\n * The returned resource can be awaited directly to fetch project info,\n * or chained to access nested resources.\n *\n * @param projectKey - The project key (e.g., `'PROJ'`)\n * @returns A chainable project resource\n *\n * @example\n * ```typescript\n * const project = await bbClient.project('PROJ');\n * const repos = await bbClient.project('PROJ').repos({ limit: 10 });\n * const prs = await bbClient.project('PROJ').repo('my-repo').pullRequests();\n * ```\n */\n project(projectKey: string): ProjectResource {\n const request: RequestFn = <T>(\n path: string,\n params?: Record<string, string | number | boolean>,\n ) => this.request<T>(path, params);\n return new ProjectResource(request, projectKey);\n }\n}\n\n/**\n * Appends query parameters to a URL string, skipping `undefined` values.\n * @internal\n */\nfunction buildUrl(base: string, params?: Record<string, string | number | boolean>): string {\n if (!params) return base;\n const entries = Object.entries(params).filter(([, v]) => v !== undefined);\n if (entries.length === 0) return base;\n const search = new URLSearchParams(entries.map(([k, v]) => [k, String(v)]));\n return `${base}?${search.toString()}`;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bitbucket-datacenter-api-client",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript client for Bitbucket Data Center REST API",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"docs": "typedoc",
|
|
21
|
+
"test": "jest",
|
|
22
|
+
"test:coverage": "jest --coverage"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"bitbucket",
|
|
26
|
+
"bitbucket-server",
|
|
27
|
+
"bitbucket-data-center",
|
|
28
|
+
"api-client",
|
|
29
|
+
"rest"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/pilmee/BitbucketDataCenterApiClient.git"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
38
|
+
"@semantic-release/git": "^10.0.1",
|
|
39
|
+
"@types/jest": "^29.5.14",
|
|
40
|
+
"jest": "^29.7.0",
|
|
41
|
+
"semantic-release": "^24.2.3",
|
|
42
|
+
"ts-jest": "^29.3.0",
|
|
43
|
+
"ts-node": "^10.9.2",
|
|
44
|
+
"tsup": "^8.4.0",
|
|
45
|
+
"typedoc": "^0.27.9",
|
|
46
|
+
"typescript": "^5.8.3"
|
|
47
|
+
}
|
|
48
|
+
}
|