@rawdash/connector-bitbucket 0.22.0 → 0.24.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.d.ts +15 -0
- package/dist/index.js +41 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -38,6 +38,11 @@ declare const bitbucketResources: {
|
|
|
38
38
|
readonly description: "Open, merged, declined, and superseded pull requests with author, source/target branches, and close timestamp.";
|
|
39
39
|
readonly endpoint: "GET /2.0/repositories/{workspace}/{repo_slug}/pullrequests?state=OPEN,MERGED,DECLINED,SUPERSEDED";
|
|
40
40
|
readonly notes: "Paginated newest-first by `updated_on`; the connector stops once a page is entirely older than `options.since`.";
|
|
41
|
+
readonly filterable: [{
|
|
42
|
+
readonly field: "state";
|
|
43
|
+
readonly ops: ["eq"];
|
|
44
|
+
readonly values: ["OPEN", "MERGED", "DECLINED", "SUPERSEDED"];
|
|
45
|
+
}];
|
|
41
46
|
readonly responses: {
|
|
42
47
|
readonly pull_requests: z.ZodObject<{
|
|
43
48
|
values: z.ZodArray<z.ZodObject<{
|
|
@@ -86,6 +91,7 @@ declare const bitbucketResources: {
|
|
|
86
91
|
readonly description: "Bitbucket Pipelines runs with state, result, target ref/commit, trigger, duration, and create/complete timestamps.";
|
|
87
92
|
readonly endpoint: "GET /2.0/repositories/{workspace}/{repo_slug}/pipelines/";
|
|
88
93
|
readonly notes: "Paginated newest-first by `created_on`; the connector stops once a page is entirely older than `options.since`.";
|
|
94
|
+
readonly filterable: [];
|
|
89
95
|
readonly responses: {
|
|
90
96
|
readonly pipelines: z.ZodObject<{
|
|
91
97
|
values: z.ZodArray<z.ZodObject<{
|
|
@@ -134,6 +140,7 @@ declare const bitbucketResources: {
|
|
|
134
140
|
readonly description: "Pipeline lifecycle events. One event per pipeline covering created_on to completed_on (or updated_on if not yet finished), tagged with the terminal state and result.";
|
|
135
141
|
readonly endpoint: "GET /2.0/repositories/{workspace}/{repo_slug}/pipelines/";
|
|
136
142
|
readonly notes: "Derived from the same pipelines response that builds the `pipeline` resource; the Bitbucket API does not expose an intermediate state-transition history endpoint.";
|
|
143
|
+
readonly filterable: [];
|
|
137
144
|
};
|
|
138
145
|
};
|
|
139
146
|
declare const id = "bitbucket";
|
|
@@ -145,6 +152,11 @@ declare class BitbucketConnector extends BaseConnector<BitbucketSettings, Bitbuc
|
|
|
145
152
|
readonly description: "Open, merged, declined, and superseded pull requests with author, source/target branches, and close timestamp.";
|
|
146
153
|
readonly endpoint: "GET /2.0/repositories/{workspace}/{repo_slug}/pullrequests?state=OPEN,MERGED,DECLINED,SUPERSEDED";
|
|
147
154
|
readonly notes: "Paginated newest-first by `updated_on`; the connector stops once a page is entirely older than `options.since`.";
|
|
155
|
+
readonly filterable: [{
|
|
156
|
+
readonly field: "state";
|
|
157
|
+
readonly ops: ["eq"];
|
|
158
|
+
readonly values: ["OPEN", "MERGED", "DECLINED", "SUPERSEDED"];
|
|
159
|
+
}];
|
|
148
160
|
readonly responses: {
|
|
149
161
|
readonly pull_requests: z.ZodObject<{
|
|
150
162
|
values: z.ZodArray<z.ZodObject<{
|
|
@@ -193,6 +205,7 @@ declare class BitbucketConnector extends BaseConnector<BitbucketSettings, Bitbuc
|
|
|
193
205
|
readonly description: "Bitbucket Pipelines runs with state, result, target ref/commit, trigger, duration, and create/complete timestamps.";
|
|
194
206
|
readonly endpoint: "GET /2.0/repositories/{workspace}/{repo_slug}/pipelines/";
|
|
195
207
|
readonly notes: "Paginated newest-first by `created_on`; the connector stops once a page is entirely older than `options.since`.";
|
|
208
|
+
readonly filterable: [];
|
|
196
209
|
readonly responses: {
|
|
197
210
|
readonly pipelines: z.ZodObject<{
|
|
198
211
|
values: z.ZodArray<z.ZodObject<{
|
|
@@ -241,6 +254,7 @@ declare class BitbucketConnector extends BaseConnector<BitbucketSettings, Bitbuc
|
|
|
241
254
|
readonly description: "Pipeline lifecycle events. One event per pipeline covering created_on to completed_on (or updated_on if not yet finished), tagged with the terminal state and result.";
|
|
242
255
|
readonly endpoint: "GET /2.0/repositories/{workspace}/{repo_slug}/pipelines/";
|
|
243
256
|
readonly notes: "Derived from the same pipelines response that builds the `pipeline` resource; the Bitbucket API does not expose an intermediate state-transition history endpoint.";
|
|
257
|
+
readonly filterable: [];
|
|
244
258
|
};
|
|
245
259
|
};
|
|
246
260
|
static readonly schemas: object & {
|
|
@@ -345,6 +359,7 @@ declare class BitbucketConnector extends BaseConnector<BitbucketSettings, Bitbuc
|
|
|
345
359
|
private activePhases;
|
|
346
360
|
private isResourceAllowed;
|
|
347
361
|
private buildPullRequestsUrl;
|
|
362
|
+
private singleSpec;
|
|
348
363
|
private buildPipelinesUrl;
|
|
349
364
|
private pullRequestsPath;
|
|
350
365
|
private pipelinesPath;
|
package/dist/index.js
CHANGED
|
@@ -204,6 +204,13 @@ var bitbucketResources = defineResources({
|
|
|
204
204
|
description: "Open, merged, declined, and superseded pull requests with author, source/target branches, and close timestamp.",
|
|
205
205
|
endpoint: "GET /2.0/repositories/{workspace}/{repo_slug}/pullrequests?state=OPEN,MERGED,DECLINED,SUPERSEDED",
|
|
206
206
|
notes: "Paginated newest-first by `updated_on`; the connector stops once a page is entirely older than `options.since`.",
|
|
207
|
+
filterable: [
|
|
208
|
+
{
|
|
209
|
+
field: "state",
|
|
210
|
+
ops: ["eq"],
|
|
211
|
+
values: ["OPEN", "MERGED", "DECLINED", "SUPERSEDED"]
|
|
212
|
+
}
|
|
213
|
+
],
|
|
207
214
|
responses: { pull_requests: pullRequestsResponseSchema }
|
|
208
215
|
},
|
|
209
216
|
pipeline: {
|
|
@@ -211,16 +218,35 @@ var bitbucketResources = defineResources({
|
|
|
211
218
|
description: "Bitbucket Pipelines runs with state, result, target ref/commit, trigger, duration, and create/complete timestamps.",
|
|
212
219
|
endpoint: "GET /2.0/repositories/{workspace}/{repo_slug}/pipelines/",
|
|
213
220
|
notes: "Paginated newest-first by `created_on`; the connector stops once a page is entirely older than `options.since`.",
|
|
221
|
+
filterable: [],
|
|
214
222
|
responses: { pipelines: pipelinesResponseSchema }
|
|
215
223
|
},
|
|
216
224
|
pipeline_event: {
|
|
217
225
|
shape: "event",
|
|
218
226
|
description: "Pipeline lifecycle events. One event per pipeline covering created_on to completed_on (or updated_on if not yet finished), tagged with the terminal state and result.",
|
|
219
227
|
endpoint: "GET /2.0/repositories/{workspace}/{repo_slug}/pipelines/",
|
|
220
|
-
notes: "Derived from the same pipelines response that builds the `pipeline` resource; the Bitbucket API does not expose an intermediate state-transition history endpoint."
|
|
228
|
+
notes: "Derived from the same pipelines response that builds the `pipeline` resource; the Bitbucket API does not expose an intermediate state-transition history endpoint.",
|
|
229
|
+
filterable: []
|
|
221
230
|
}
|
|
222
231
|
});
|
|
223
232
|
var id = "bitbucket";
|
|
233
|
+
var PULL_REQUEST_STATES = /* @__PURE__ */ new Set([
|
|
234
|
+
"OPEN",
|
|
235
|
+
"MERGED",
|
|
236
|
+
"DECLINED",
|
|
237
|
+
"SUPERSEDED"
|
|
238
|
+
]);
|
|
239
|
+
function pushablePullRequestState(filter) {
|
|
240
|
+
if (!filter) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
for (const clause of filter) {
|
|
244
|
+
if ("field" in clause && clause.field === "state" && clause.op === "eq" && typeof clause.value === "string" && PULL_REQUEST_STATES.has(clause.value)) {
|
|
245
|
+
return clause.value;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
224
250
|
var BitbucketConnector = class _BitbucketConnector extends BaseConnector {
|
|
225
251
|
static id = id;
|
|
226
252
|
static resources = bitbucketResources;
|
|
@@ -311,18 +337,25 @@ var BitbucketConnector = class _BitbucketConnector extends BaseConnector {
|
|
|
311
337
|
// -------------------------------------------------------------------------
|
|
312
338
|
// URL builders
|
|
313
339
|
// -------------------------------------------------------------------------
|
|
314
|
-
buildPullRequestsUrl(repoSlugValue, options) {
|
|
340
|
+
buildPullRequestsUrl(repoSlugValue, options, spec) {
|
|
315
341
|
const u = new URL(
|
|
316
342
|
`${API_BASE}/2.0/repositories/${encodeURIComponent(this.settings.workspace)}/${encodeURIComponent(repoSlugValue)}/pullrequests`
|
|
317
343
|
);
|
|
318
344
|
u.searchParams.set("pagelen", String(PAGE_SIZE));
|
|
319
345
|
u.searchParams.set("sort", "-updated_on");
|
|
320
|
-
u.searchParams.set(
|
|
346
|
+
u.searchParams.set(
|
|
347
|
+
"state",
|
|
348
|
+
pushablePullRequestState(spec?.filter) ?? "OPEN,MERGED,DECLINED,SUPERSEDED"
|
|
349
|
+
);
|
|
321
350
|
if (options.since) {
|
|
322
351
|
u.searchParams.set("q", `updated_on >= ${options.since}`);
|
|
323
352
|
}
|
|
324
353
|
return u.toString();
|
|
325
354
|
}
|
|
355
|
+
singleSpec(options, resource) {
|
|
356
|
+
const specs = options.fetchSpecs?.[resource];
|
|
357
|
+
return specs && specs.length === 1 ? specs[0] : void 0;
|
|
358
|
+
}
|
|
326
359
|
buildPipelinesUrl(repoSlugValue, options) {
|
|
327
360
|
const u = new URL(
|
|
328
361
|
`${API_BASE}/2.0/repositories/${encodeURIComponent(this.settings.workspace)}/${encodeURIComponent(repoSlugValue)}/pipelines/`
|
|
@@ -354,7 +387,11 @@ var BitbucketConnector = class _BitbucketConnector extends BaseConnector {
|
|
|
354
387
|
}
|
|
355
388
|
const slug = repos[idx];
|
|
356
389
|
const expectedPath = this.pullRequestsPath(slug);
|
|
357
|
-
const fetchUrl = this.sanitizeUrl(rawPageUrl, expectedPath) ?? this.buildPullRequestsUrl(
|
|
390
|
+
const fetchUrl = this.sanitizeUrl(rawPageUrl, expectedPath) ?? this.buildPullRequestsUrl(
|
|
391
|
+
slug,
|
|
392
|
+
options,
|
|
393
|
+
this.singleSpec(options, "pull_request")
|
|
394
|
+
);
|
|
358
395
|
const res = await this.fetch(
|
|
359
396
|
fetchUrl,
|
|
360
397
|
"pull_requests",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../connector-shared/src/errors.ts","../../../connector-shared/src/retry.ts","../../../connector-shared/src/version.ts","../../../connector-shared/src/request.ts","../../../connector-shared/src/rate-limit.ts","../../../connector-shared/src/map-concurrent.ts","../../../connector-shared/src/sanitize.ts","../../../connector-shared/src/epoch.ts","../../../connector-shared/src/pagination.ts","../../../connector-shared/src/logger.ts","../src/bitbucket.ts","../src/index.ts"],"sourcesContent":["import type { HttpResponse } from './types';\n\nexport type HttpErrorKind =\n | 'transient'\n | 'rate_limit'\n | 'auth'\n | 'upstream_bug'\n | 'client_bug';\n\nexport abstract class HttpClientError extends Error {\n abstract readonly kind: HttpErrorKind;\n readonly response?: HttpResponse;\n\n constructor(message: string, response?: HttpResponse) {\n super(message);\n this.name = new.target.name;\n this.response = response;\n }\n}\n\nexport class TransientError extends HttpClientError {\n readonly kind = 'transient' as const;\n}\n\nexport class RateLimitError extends HttpClientError {\n readonly kind = 'rate_limit' as const;\n readonly retryAfter?: Date;\n\n constructor(message: string, response?: HttpResponse, retryAfter?: Date) {\n super(message, response);\n this.retryAfter = retryAfter;\n }\n}\n\nexport class AuthError extends HttpClientError {\n readonly kind = 'auth' as const;\n}\n\nexport class UpstreamBugError extends HttpClientError {\n readonly kind = 'upstream_bug' as const;\n}\n\nexport class ClientBugError extends HttpClientError {\n readonly kind = 'client_bug' as const;\n}\n\nexport function classifyStatus(status: number): HttpErrorKind {\n if (status === 429) {\n return 'rate_limit';\n }\n if (status === 401 || status === 403) {\n return 'auth';\n }\n if (status === 408) {\n return 'transient';\n }\n if (status >= 500) {\n return 'upstream_bug';\n }\n if (status >= 400) {\n return 'client_bug';\n }\n return 'client_bug';\n}\n\nexport function errorForStatus(\n message: string,\n response: HttpResponse,\n retryAfter?: Date,\n): HttpClientError {\n const kind = classifyStatus(response.status);\n switch (kind) {\n case 'rate_limit':\n return new RateLimitError(message, response, retryAfter);\n case 'auth':\n return new AuthError(message, response);\n case 'transient':\n return new TransientError(message, response);\n case 'upstream_bug':\n return new UpstreamBugError(message, response);\n case 'client_bug':\n return new ClientBugError(message, response);\n }\n}\n","import { HttpClientError, RateLimitError, TransientError } from './errors';\n\nexport interface RetryPolicy {\n maxAttempts?: number;\n initialDelayMs?: number;\n maxDelayMs?: number;\n retryOn?: (status: number | null, err?: Error) => boolean;\n}\n\nexport const defaultRetryOn = (status: number | null, err?: Error): boolean => {\n if (err instanceof RateLimitError) {\n return true;\n }\n if (err instanceof TransientError) {\n return true;\n }\n if (status === null) {\n return err instanceof Error && !(err instanceof HttpClientError);\n }\n if (status === 408 || status === 429) {\n return true;\n }\n if (status >= 500) {\n return true;\n }\n return false;\n};\n\nexport function backoffDelayMs(\n attempt: number,\n policy: Required<Pick<RetryPolicy, 'initialDelayMs' | 'maxDelayMs'>>,\n): number {\n const base = policy.initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, policy.maxDelayMs);\n}\n\nexport function parseRetryAfter(\n headerValue: string | null,\n now: Date = new Date(),\n): Date | undefined {\n if (!headerValue) {\n return undefined;\n }\n const trimmed = headerValue.trim();\n if (/^\\d+$/.test(trimmed)) {\n return new Date(now.getTime() + Number(trimmed) * 1000);\n }\n const parsed = Date.parse(trimmed);\n if (Number.isNaN(parsed)) {\n return undefined;\n }\n return new Date(parsed);\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.reject(signal.reason ?? new Error('Aborted'));\n }\n return new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal!.reason ?? new Error('Aborted'));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n","export const HTTP_CLIENT_VERSION = '0.0.0';\n\nexport const DEFAULT_USER_AGENT = `rawdash-connector/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n\nexport function connectorUserAgent(connectorId: string): string {\n return `rawdash-connector-${connectorId}/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n}\n","import {\n AuthError,\n ClientBugError,\n HttpClientError,\n RateLimitError,\n TransientError,\n UpstreamBugError,\n errorForStatus,\n} from './errors';\nimport { defaultRetryOn, parseRetryAfter, sleep } from './retry';\nimport type { FetchLike, HttpMethod, HttpRequest, HttpResponse } from './types';\nimport { DEFAULT_USER_AGENT } from './version';\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_ATTEMPTS = 3;\nconst DEFAULT_INITIAL_DELAY_MS = 1000;\nconst DEFAULT_MAX_DELAY_MS = 60_000;\nconst OBSERVER_TIMEOUT_MS = 250;\n\nexport interface RequestObservation {\n url: string;\n method: HttpMethod;\n status: number;\n resource: string;\n requestId: string;\n body: unknown;\n}\n\nexport type RequestObserver = (\n event: RequestObservation,\n) => void | Promise<void>;\n\nexport interface RequestOptions {\n fetch?: FetchLike;\n observer?: RequestObserver;\n resource: string;\n requestId?: string;\n}\n\nasync function notifyObserver(\n observer: RequestObserver,\n event: RequestObservation,\n): Promise<void> {\n let result: void | Promise<void>;\n try {\n result = observer(event);\n } catch (err) {\n console.warn('[connector-shared] request observer threw:', err);\n return;\n }\n if (!(result instanceof Promise)) {\n return;\n }\n const guarded = result.catch((err) => {\n console.warn('[connector-shared] request observer rejected:', err);\n });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, OBSERVER_TIMEOUT_MS);\n });\n try {\n await Promise.race([guarded, timeout]);\n } finally {\n if (timer) {\n clearTimeout(timer);\n }\n }\n}\n\nfunction newRequestId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) {\n return c.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction mergeHeaders(\n defaults: Record<string, string>,\n overrides: Record<string, string> | undefined,\n): Record<string, string> {\n const merged: Record<string, string> = {};\n for (const [k, v] of Object.entries(defaults)) {\n merged[k.toLowerCase()] = v;\n }\n if (overrides) {\n for (const [k, v] of Object.entries(overrides)) {\n merged[k.toLowerCase()] = v;\n }\n }\n return merged;\n}\n\nfunction linkTimeoutSignal(\n parent: AbortSignal | undefined,\n timeoutMs: number,\n): { signal: AbortSignal; cancel: () => void } {\n const controller = new AbortController();\n const onParentAbort = () => {\n controller.abort(parent?.reason);\n };\n if (parent) {\n if (parent.aborted) {\n controller.abort(parent.reason);\n } else {\n parent.addEventListener('abort', onParentAbort, { once: true });\n }\n }\n const timer = setTimeout(() => {\n controller.abort(new Error(`Request timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n return {\n signal: controller.signal,\n cancel: () => {\n clearTimeout(timer);\n if (parent) {\n parent.removeEventListener('abort', onParentAbort);\n }\n },\n };\n}\n\nasync function readBody(res: Response, parseJson: boolean): Promise<unknown> {\n if (res.status === 204 || res.status === 205) {\n return null;\n }\n const contentType = res.headers.get('content-type') ?? '';\n if (parseJson && contentType.includes('application/json')) {\n const text = await res.text();\n if (text.length === 0) {\n return null;\n }\n return JSON.parse(text);\n }\n return res.text();\n}\n\nexport async function request<T = unknown>(\n req: HttpRequest,\n options: RequestOptions,\n): Promise<HttpResponse<T>> {\n const fetchImpl: FetchLike = options.fetch ?? (globalThis.fetch as FetchLike);\n const retry = req.retry ?? {};\n const maxAttempts = retry.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;\n const initialDelayMs = retry.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;\n const maxDelayMs = retry.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;\n const retryOn = retry.retryOn ?? defaultRetryOn;\n const timeoutMs = req.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const parseJson = req.parseJson ?? true;\n\n const headers = mergeHeaders(\n {\n 'User-Agent': DEFAULT_USER_AGENT,\n Accept: 'application/json',\n },\n req.headers,\n );\n\n let lastErr: Error | undefined;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n req.signal?.throwIfAborted();\n\n const { signal, cancel } = linkTimeoutSignal(req.signal, timeoutMs);\n let res: Response;\n try {\n res = await fetchImpl(req.url, {\n method: req.method ?? 'GET',\n headers,\n body: req.body as RequestInit['body'],\n signal,\n });\n } catch (err) {\n cancel();\n if (req.signal?.aborted) {\n throw req.signal.reason ?? err;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n lastErr = error;\n if (attempt < maxAttempts - 1 && retryOn(null, error)) {\n const delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n await sleep(delay, req.signal);\n continue;\n }\n throw new TransientError(error.message);\n }\n cancel();\n\n const body = await readBody(res, parseJson);\n const httpResponse: HttpResponse<T> = {\n status: res.status,\n headers: res.headers,\n body: body as T,\n };\n if (req.rateLimit) {\n const state = req.rateLimit.parse(res.headers);\n if (state) {\n httpResponse.rateLimitState = state;\n }\n }\n\n if (options.observer) {\n await notifyObserver(options.observer, {\n url: req.url,\n method: req.method ?? 'GET',\n status: res.status,\n resource: options.resource,\n requestId: options.requestId ?? newRequestId(),\n body,\n });\n }\n\n if (res.ok) {\n return httpResponse;\n }\n\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\n const message = `HTTP ${res.status} ${res.statusText} for ${req.method ?? 'GET'} ${req.url}`;\n const err = errorForStatus(message, httpResponse, retryAfter);\n\n if (\n attempt < maxAttempts - 1 &&\n retryOn(res.status, err) &&\n !(err instanceof AuthError) &&\n !(err instanceof ClientBugError)\n ) {\n lastErr = err;\n let delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n if (err instanceof RateLimitError && retryAfter) {\n const wait = retryAfter.getTime() - Date.now();\n if (wait > 0) {\n delay = Math.min(wait, maxDelayMs);\n }\n }\n await sleep(delay, req.signal);\n continue;\n }\n\n throw err;\n }\n\n throw lastErr ?? new UpstreamBugError('Exhausted retry attempts');\n}\n\nfunction computeDelay(\n attempt: number,\n initialDelayMs: number,\n maxDelayMs: number,\n): number {\n const base = initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, maxDelayMs);\n}\n\nexport { HttpClientError };\n","export interface RateLimitState {\n remaining: number;\n resetAt: Date;\n}\n\nexport interface RateLimitPolicy {\n parse(headers: Headers): RateLimitState | null;\n}\n\nexport interface StandardRateLimitPolicyConfig {\n remainingHeader: string;\n resetHeader: string;\n resetUnit: 's' | 'ms';\n resetFallbackMs?: number;\n}\n\nexport function standardRateLimitPolicy(\n config: StandardRateLimitPolicyConfig,\n): RateLimitPolicy {\n const { remainingHeader, resetHeader, resetUnit, resetFallbackMs } = config;\n const multiplier = resetUnit === 's' ? 1000 : 1;\n return {\n parse(h) {\n const remainingRaw = h.get(remainingHeader);\n if (remainingRaw === null || remainingRaw.trim() === '') {\n return null;\n }\n const remaining = Number(remainingRaw);\n if (!Number.isFinite(remaining)) {\n return null;\n }\n const resetRaw = h.get(resetHeader);\n if (resetRaw === null) {\n if (resetFallbackMs === undefined) {\n return null;\n }\n return {\n remaining,\n resetAt: new Date(Date.now() + resetFallbackMs),\n };\n }\n if (resetRaw.trim() === '') {\n return null;\n }\n const reset = Number(resetRaw);\n if (!Number.isFinite(reset) || reset < 0) {\n return null;\n }\n const resetMs = reset * multiplier;\n if (!Number.isFinite(resetMs)) {\n return null;\n }\n return { remaining, resetAt: new Date(resetMs) };\n },\n };\n}\n","export async function mapWithConcurrency<T, R>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n if (items.length === 0) {\n return results;\n }\n const normalized = Number.isFinite(concurrency) ? Math.floor(concurrency) : 1;\n const limit = Math.max(1, Math.min(normalized, items.length));\n let next = 0;\n let failed = false;\n\n async function worker(): Promise<void> {\n while (!failed) {\n const i = next++;\n if (i >= items.length) {\n return;\n }\n try {\n results[i] = await fn(items[i]!, i);\n } catch (err) {\n failed = true;\n throw err;\n }\n }\n }\n\n const workers: Promise<void>[] = [];\n for (let w = 0; w < limit; w++) {\n workers.push(worker());\n }\n await Promise.all(workers);\n return results;\n}\n","export interface SanitizeAllowedUrlOptions {\n url: string | null;\n host: string;\n pathname: string;\n protocol?: 'https:' | 'http:';\n}\n\nexport function sanitizeAllowedUrl(\n options: SanitizeAllowedUrlOptions,\n): string | null {\n const { url, host, pathname, protocol = 'https:' } = options;\n if (url === null) {\n return null;\n }\n try {\n const u = new URL(url);\n if (u.protocol !== protocol || u.host !== host || u.pathname !== pathname) {\n return null;\n }\n return u.toString();\n } catch {\n return null;\n }\n}\n","export type EpochUnit = 'ms' | 's' | 'iso';\n\nexport function parseEpoch(\n value: number | string | null | undefined,\n unit: EpochUnit,\n): number | null {\n if (value === null || value === undefined) {\n return null;\n }\n if (unit === 'iso') {\n if (typeof value !== 'string') {\n return null;\n }\n const ms = new Date(value).getTime();\n return Number.isFinite(ms) ? ms : null;\n }\n if (typeof value === 'string' && value.trim() === '') {\n return null;\n }\n const n = typeof value === 'number' ? value : Number(value);\n if (!Number.isFinite(n)) {\n return null;\n }\n const result = unit === 's' ? n * 1000 : n;\n return Number.isFinite(result) ? result : null;\n}\n","import { request } from './request';\nimport type { HttpRequest } from './types';\n\nexport function parseLinkHeader(header: string | null): Record<string, string> {\n if (!header) {\n return {};\n }\n const result: Record<string, string> = {};\n for (const part of header.split(',')) {\n const match = part.match(/<([^>]+)>\\s*;\\s*rel=\"([^\"]+)\"/);\n if (match) {\n result[match[2]!] = match[1]!;\n }\n }\n return result;\n}\n\nexport async function* paginateLink<T>(\n initial: HttpRequest,\n parse: (body: unknown) => T[],\n options: { resource: string },\n): AsyncIterable<T> {\n let next: string | null = initial.url;\n while (next) {\n const res: Awaited<ReturnType<typeof request>> = await request(\n {\n ...initial,\n url: next,\n },\n { resource: options.resource },\n );\n for (const item of parse(res.body)) {\n yield item;\n }\n const links = parseLinkHeader(res.headers.get('link'));\n next = links['next'] ?? null;\n }\n}\n\nexport async function* paginateCursor<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; nextCursor: string | null },\n buildNext: (req: HttpRequest, cursor: string) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let req: HttpRequest = initial;\n while (true) {\n const res = await request(req, { resource: options.resource });\n const { items, nextCursor } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!nextCursor) {\n return;\n }\n req = buildNext(req, nextCursor);\n }\n}\n\nexport async function* paginatePage<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; hasMore: boolean },\n buildPage: (req: HttpRequest, page: number) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let page = 1;\n while (true) {\n const req = page === 1 ? initial : buildPage(initial, page);\n const res = await request(req, { resource: options.resource });\n const { items, hasMore } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!hasMore || items.length === 0) {\n return;\n }\n page++;\n }\n}\n","export type LogFields = Record<string, unknown>;\n\nexport interface ConnectorLogger {\n info(event: string, fields?: LogFields): void;\n warn(event: string, fields?: LogFields): void;\n}\n\nexport interface ConnectorLoggerOptions {\n scope: string;\n}\n\nconst MAX_VALUE_LEN = 120;\n\nfunction truncate(s: string, max = MAX_VALUE_LEN): string {\n if (s.length <= max) {\n return s;\n }\n return `${s.slice(0, max - 1)}…`;\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return '';\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (typeof value === 'string') {\n const t = truncate(value);\n if (/[\\s\"=]/.test(t)) {\n return JSON.stringify(t);\n }\n return t;\n }\n if (typeof value === 'bigint') {\n return value.toString();\n }\n let json: string | undefined;\n try {\n json = JSON.stringify(value);\n } catch {\n json = undefined;\n }\n return truncate(json ?? String(value));\n}\n\nexport function formatLogFields(fields?: LogFields): string {\n if (!fields) {\n return '';\n }\n const parts: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (v === undefined) {\n continue;\n }\n parts.push(`${k}=${formatValue(v)}`);\n }\n return parts.length > 0 ? ` ${parts.join(' ')}` : '';\n}\n\nexport function formatLogLine(\n scope: string,\n event: string,\n fields?: LogFields,\n): string {\n return `[${scope}] ${event}${formatLogFields(fields)}`;\n}\n\nexport function createDefaultConnectorLogger(\n opts: ConnectorLoggerOptions,\n): ConnectorLogger {\n return {\n info(event, fields) {\n console.info(formatLogLine(opts.scope, event, fields));\n },\n warn(event, fields) {\n console.warn(formatLogLine(opts.scope, event, fields));\n },\n };\n}\n\nconst NOOP_LOGGER: ConnectorLogger = {\n info() {},\n warn() {},\n};\n\nexport function noopConnectorLogger(): ConnectorLogger {\n return NOOP_LOGGER;\n}\n","import {\n type HttpResponse,\n connectorUserAgent,\n parseEpoch,\n} from '@rawdash/connector-shared';\nimport {\n BaseConnector,\n type ChunkedSyncCursor,\n type ConnectorContext,\n type ConnectorDoc,\n type CredentialsSchema,\n type FetchPageResult,\n type StorageHandle,\n type SyncOptions,\n type SyncResult,\n defineConfigFields,\n defineConnectorDoc,\n defineResources,\n makeChunkedCursorGuard,\n paginateChunked,\n schemasFromResources,\n selectActivePhases,\n} from '@rawdash/core';\nimport { z } from 'zod';\n\n// ---------------------------------------------------------------------------\n// configFields\n// ---------------------------------------------------------------------------\n\nconst repoSlug = z\n .string()\n .min(1)\n .regex(\n /^[^/\\s?#]+$/,\n 'Use the repository slug only (no workspace prefix, slashes, or query).',\n );\n\nexport const configFields = defineConfigFields(\n z.object({\n workspace: z\n .string()\n .min(1)\n .regex(\n /^[^/\\s?#]+$/,\n 'Use the workspace slug only (no slashes, spaces, or query).',\n )\n .meta({\n label: 'Workspace',\n description:\n 'Bitbucket Cloud workspace slug (the segment shown in repo URLs after bitbucket.org/).',\n placeholder: 'my-workspace',\n }),\n username: z.string().min(1).meta({\n label: 'Atlassian username',\n description:\n 'Atlassian account username paired with the app password for Basic auth (find it under Personal settings -> Account settings).',\n placeholder: 'janedoe',\n }),\n appPassword: z.object({ $secret: z.string() }).meta({\n label: 'App password',\n description:\n 'Bitbucket app password with `Repositories:Read` and `Pipelines:Read` scopes. Create one at Personal settings -> App passwords.',\n placeholder: 'ATBB...',\n secret: true,\n }),\n repoSlugs: z\n .array(repoSlug)\n .nonempty()\n .refine((slugs) => new Set(slugs).size === slugs.length, {\n error: 'Repository slugs must be unique.',\n })\n .meta({\n label: 'Repository slugs',\n description:\n 'Repositories to sync, named by their slug within the workspace (no `workspace/` prefix).',\n }),\n resources: z\n .array(z.enum(['pull_request', 'pipeline', 'pipeline_event']))\n .nonempty()\n .optional()\n .meta({\n label: 'Resources',\n description:\n \"Which Bitbucket resources to sync. Omit to sync all of them. 'pipeline_event' rides the 'pipeline' phase - enabling it without 'pipeline' still fetches pipelines but skips writing pipeline entities.\",\n }),\n }),\n);\n\n// ---------------------------------------------------------------------------\n// Connector documentation metadata\n// ---------------------------------------------------------------------------\n\nexport const doc: ConnectorDoc = defineConnectorDoc({\n displayName: 'Bitbucket',\n category: 'engineering',\n brandColor: '#0052CC',\n tagline:\n 'Sync pull requests, pipelines, and pipeline lifecycle events from Bitbucket Cloud repositories.',\n vendor: {\n name: 'Atlassian',\n domain: 'bitbucket.org',\n apiDocs: 'https://developer.atlassian.com/cloud/bitbucket/rest/intro/',\n website: 'https://bitbucket.org',\n },\n auth: {\n summary:\n 'Authenticates over HTTP Basic auth using an Atlassian account username and a Bitbucket app password. The password is scoped to the projects and repositories the account can already read.',\n setup: [\n 'Open Bitbucket -> Personal settings -> App passwords (https://bitbucket.org/account/settings/app-passwords/).',\n 'Create an app password with `Repositories:Read` and `Pipelines:Read` scopes.',\n 'Store it as a secret and reference it from the connector config as `appPassword: secret(\"BITBUCKET_APP_PASSWORD\")`, alongside your `workspace`, `username`, and the list of `repoSlugs` to sync.',\n ],\n },\n rateLimit:\n 'Bitbucket Cloud applies hourly per-IP and per-user limits (around 1,000 requests/hour for app-password auth). Pagination uses a `next` URL in each response and a configurable `pagelen` (capped at 50 here).',\n limitations: [\n 'Bitbucket Server / Data Center are out of scope; this connector targets Bitbucket Cloud only.',\n 'Pipeline state-transition events are synthesized: one `pipeline_event` is emitted per pipeline lifecycle (created_on to completed_on/updated_on), not one per intermediate state change.',\n 'Repository discovery is not automatic - configure each repository slug explicitly via `repoSlugs`.',\n ],\n});\n\n// ---------------------------------------------------------------------------\n// Settings and credentials\n// ---------------------------------------------------------------------------\n\nexport type BitbucketResource = 'pull_request' | 'pipeline' | 'pipeline_event';\n\nexport interface BitbucketSettings {\n workspace: string;\n repoSlugs: readonly string[];\n resources?: readonly BitbucketResource[];\n}\n\nconst bitbucketCredentials = {\n username: {\n description: 'Atlassian account username',\n auth: 'required' as const,\n },\n appPassword: {\n description: 'Bitbucket app password',\n auth: 'required' as const,\n },\n} satisfies CredentialsSchema;\n\ntype BitbucketCredentials = typeof bitbucketCredentials;\n\nconst API_HOST = 'api.bitbucket.org';\nconst API_BASE = `https://${API_HOST}`;\nconst PAGE_SIZE = 50;\n\n// ---------------------------------------------------------------------------\n// Sync phases + cursor\n// ---------------------------------------------------------------------------\n\nconst PHASE_ORDER = ['pull_requests', 'pipelines'] as const;\n\ntype BitbucketPhase = (typeof PHASE_ORDER)[number];\n\ntype BitbucketSyncCursor = ChunkedSyncCursor<BitbucketPhase, string>;\n\nconst isBitbucketSyncCursor = makeChunkedCursorGuard(PHASE_ORDER);\n\n// Page-cursor encoding: `<repoIdx>|<pageUrl?>`.\n// - null page -> start at repoIdx=0 with no URL yet\n// - \"<idx>|\" -> start of repo at idx, build initial URL\n// - \"<idx>|<url>\" -> continuing pagination for repo at idx\nfunction decodePage(page: string | null): {\n idx: number;\n url: string | null;\n} {\n if (page === null) {\n return { idx: 0, url: null };\n }\n const sep = page.indexOf('|');\n if (sep === -1) {\n return { idx: 0, url: null };\n }\n const idxRaw = Number.parseInt(page.slice(0, sep), 10);\n const url = page.slice(sep + 1);\n return {\n idx: Number.isFinite(idxRaw) && idxRaw >= 0 ? idxRaw : 0,\n url: url === '' ? null : url,\n };\n}\n\nfunction encodePage(idx: number, url: string | null): string {\n return `${idx}|${url ?? ''}`;\n}\n\n// ---------------------------------------------------------------------------\n// Bitbucket API types\n// ---------------------------------------------------------------------------\n\ninterface BitbucketAccountRef {\n uuid?: string | null;\n display_name?: string | null;\n nickname?: string | null;\n account_id?: string | null;\n}\n\ninterface BitbucketBranchRef {\n branch?: { name?: string | null } | null;\n commit?: { hash?: string | null } | null;\n}\n\ninterface BitbucketPullRequest {\n id: number;\n title: string;\n state: string;\n author?: BitbucketAccountRef | null;\n source?: BitbucketBranchRef | null;\n destination?: BitbucketBranchRef | null;\n created_on: string;\n updated_on: string;\n closed_on?: string | null;\n links?: { html?: { href?: string | null } | null } | null;\n}\n\ninterface BitbucketPullRequestsResponse {\n values: BitbucketPullRequest[];\n next?: string | null;\n page?: number | null;\n pagelen?: number | null;\n}\n\ninterface BitbucketPipelineTarget {\n ref_name?: string | null;\n commit?: { hash?: string | null } | null;\n selector?: { type?: string | null; pattern?: string | null } | null;\n}\n\ninterface BitbucketPipelineState {\n name: string;\n type?: string | null;\n result?: { name?: string | null } | null;\n}\n\ninterface BitbucketPipeline {\n uuid: string;\n build_number: number;\n state: BitbucketPipelineState;\n creator?: BitbucketAccountRef | null;\n target?: BitbucketPipelineTarget | null;\n trigger?: { name?: string | null; type?: string | null } | null;\n created_on: string;\n completed_on?: string | null;\n duration_in_seconds?: number | null;\n build_seconds_used?: number | null;\n}\n\ninterface BitbucketPipelinesResponse {\n values: BitbucketPipeline[];\n next?: string | null;\n page?: number | null;\n pagelen?: number | null;\n}\n\n// ---------------------------------------------------------------------------\n// Zod response schemas\n// ---------------------------------------------------------------------------\n\nconst accountRefSchema = z.object({\n uuid: z.string().nullable().optional(),\n display_name: z.string().nullable().optional(),\n nickname: z.string().nullable().optional(),\n account_id: z.string().nullable().optional(),\n});\n\nconst branchRefSchema = z.object({\n branch: z\n .object({ name: z.string().nullable().optional() })\n .nullable()\n .optional(),\n commit: z\n .object({ hash: z.string().nullable().optional() })\n .nullable()\n .optional(),\n});\n\nconst pullRequestSchema = z.object({\n id: z.number().int().nonnegative(),\n title: z.string(),\n state: z.string().min(1),\n author: accountRefSchema.nullable().optional(),\n source: branchRefSchema.nullable().optional(),\n destination: branchRefSchema.nullable().optional(),\n created_on: z.iso.datetime(),\n updated_on: z.iso.datetime(),\n closed_on: z.iso.datetime().nullable().optional(),\n links: z\n .object({\n html: z\n .object({ href: z.string().nullable().optional() })\n .nullable()\n .optional(),\n })\n .nullable()\n .optional(),\n});\n\nconst pullRequestsResponseSchema = z.object({\n values: z.array(pullRequestSchema),\n next: z.string().nullable().optional(),\n page: z.number().int().nullable().optional(),\n pagelen: z.number().int().nullable().optional(),\n});\n\nconst pipelineStateSchema = z.object({\n name: z.string().min(1),\n type: z.string().nullable().optional(),\n result: z\n .object({ name: z.string().nullable().optional() })\n .nullable()\n .optional(),\n});\n\nconst pipelineTargetSchema = z.object({\n ref_name: z.string().nullable().optional(),\n commit: z\n .object({ hash: z.string().nullable().optional() })\n .nullable()\n .optional(),\n selector: z\n .object({\n type: z.string().nullable().optional(),\n pattern: z.string().nullable().optional(),\n })\n .nullable()\n .optional(),\n});\n\nconst pipelineSchema = z.object({\n uuid: z.string().min(1),\n build_number: z.number().int().nonnegative(),\n state: pipelineStateSchema,\n creator: accountRefSchema.nullable().optional(),\n target: pipelineTargetSchema.nullable().optional(),\n trigger: z\n .object({\n name: z.string().nullable().optional(),\n type: z.string().nullable().optional(),\n })\n .nullable()\n .optional(),\n created_on: z.iso.datetime(),\n completed_on: z.iso.datetime().nullable().optional(),\n duration_in_seconds: z.number().nullable().optional(),\n build_seconds_used: z.number().nullable().optional(),\n});\n\nconst pipelinesResponseSchema = z.object({\n values: z.array(pipelineSchema),\n next: z.string().nullable().optional(),\n page: z.number().int().nullable().optional(),\n pagelen: z.number().int().nullable().optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Resource definitions\n// ---------------------------------------------------------------------------\n\nexport const bitbucketResources = defineResources({\n pull_request: {\n shape: 'entity',\n description:\n 'Open, merged, declined, and superseded pull requests with author, source/target branches, and close timestamp.',\n endpoint:\n 'GET /2.0/repositories/{workspace}/{repo_slug}/pullrequests?state=OPEN,MERGED,DECLINED,SUPERSEDED',\n notes:\n 'Paginated newest-first by `updated_on`; the connector stops once a page is entirely older than `options.since`.',\n responses: { pull_requests: pullRequestsResponseSchema },\n },\n pipeline: {\n shape: 'entity',\n description:\n 'Bitbucket Pipelines runs with state, result, target ref/commit, trigger, duration, and create/complete timestamps.',\n endpoint: 'GET /2.0/repositories/{workspace}/{repo_slug}/pipelines/',\n notes:\n 'Paginated newest-first by `created_on`; the connector stops once a page is entirely older than `options.since`.',\n responses: { pipelines: pipelinesResponseSchema },\n },\n pipeline_event: {\n shape: 'event',\n description:\n 'Pipeline lifecycle events. One event per pipeline covering created_on to completed_on (or updated_on if not yet finished), tagged with the terminal state and result.',\n endpoint: 'GET /2.0/repositories/{workspace}/{repo_slug}/pipelines/',\n notes:\n 'Derived from the same pipelines response that builds the `pipeline` resource; the Bitbucket API does not expose an intermediate state-transition history endpoint.',\n },\n});\n\nexport const id = 'bitbucket';\n\n// ---------------------------------------------------------------------------\n// Connector class\n// ---------------------------------------------------------------------------\n\ninterface RepoBatch<T> {\n repoSlug: string;\n items: T[];\n}\n\nexport class BitbucketConnector extends BaseConnector<\n BitbucketSettings,\n BitbucketCredentials\n> {\n static readonly id = id;\n\n static readonly resources = bitbucketResources;\n\n static readonly schemas = schemasFromResources(bitbucketResources);\n\n static create(input: unknown, ctx?: ConnectorContext): BitbucketConnector {\n const parsed = configFields.parse(input);\n return new BitbucketConnector(\n {\n workspace: parsed.workspace,\n repoSlugs: parsed.repoSlugs,\n resources: parsed.resources,\n },\n { username: parsed.username, appPassword: parsed.appPassword },\n ctx,\n );\n }\n\n readonly id = id;\n override readonly credentials = bitbucketCredentials;\n\n private buildHeaders(): Record<string, string> {\n const basic = btoa(`${this.creds.username}:${this.creds.appPassword}`);\n return {\n Authorization: `Basic ${basic}`,\n Accept: 'application/json',\n 'User-Agent': connectorUserAgent('bitbucket'),\n };\n }\n\n private fetch<T>(\n url: string,\n resource: string,\n signal: AbortSignal | undefined,\n ): Promise<HttpResponse<T>> {\n return this.get<T>(url, {\n resource,\n headers: this.buildHeaders(),\n signal,\n });\n }\n\n private sanitizeUrl(url: string | null, expectedPath: string): string | null {\n if (!url) {\n return null;\n }\n try {\n const u = new URL(url);\n if (u.protocol !== 'https:' || u.host !== API_HOST) {\n return null;\n }\n if (u.pathname !== expectedPath) {\n return null;\n }\n return u.toString();\n } catch {\n return null;\n }\n }\n\n private static readonly PHASE_RESOURCES: Record<\n BitbucketPhase,\n readonly BitbucketResource[]\n > = {\n pull_requests: ['pull_request'],\n pipelines: ['pipeline', 'pipeline_event'],\n };\n\n private activePhases(\n optionsResources: ReadonlySet<string> | undefined,\n ): BitbucketPhase[] {\n const fromSettings = selectActivePhases<BitbucketResource, BitbucketPhase>(\n (r) => {\n switch (r) {\n case 'pull_request':\n return 'pull_requests';\n case 'pipeline':\n case 'pipeline_event':\n return 'pipelines';\n }\n },\n PHASE_ORDER,\n this.settings.resources,\n );\n if (optionsResources === undefined) {\n return fromSettings;\n }\n return fromSettings.filter((phase) =>\n BitbucketConnector.PHASE_RESOURCES[phase].some((r) =>\n optionsResources.has(r),\n ),\n );\n }\n\n private isResourceAllowed(\n resource: BitbucketResource,\n optionsResources: ReadonlySet<string> | undefined,\n ): boolean {\n const fromSettings = this.settings.resources;\n if (\n fromSettings &&\n fromSettings.length > 0 &&\n !fromSettings.includes(resource)\n ) {\n return false;\n }\n if (optionsResources !== undefined && !optionsResources.has(resource)) {\n return false;\n }\n return true;\n }\n\n // -------------------------------------------------------------------------\n // URL builders\n // -------------------------------------------------------------------------\n\n private buildPullRequestsUrl(\n repoSlugValue: string,\n options: SyncOptions,\n ): string {\n const u = new URL(\n `${API_BASE}/2.0/repositories/${encodeURIComponent(this.settings.workspace)}/${encodeURIComponent(repoSlugValue)}/pullrequests`,\n );\n u.searchParams.set('pagelen', String(PAGE_SIZE));\n u.searchParams.set('sort', '-updated_on');\n u.searchParams.set('state', 'OPEN,MERGED,DECLINED,SUPERSEDED');\n if (options.since) {\n u.searchParams.set('q', `updated_on >= ${options.since}`);\n }\n return u.toString();\n }\n\n private buildPipelinesUrl(\n repoSlugValue: string,\n options: SyncOptions,\n ): string {\n const u = new URL(\n `${API_BASE}/2.0/repositories/${encodeURIComponent(this.settings.workspace)}/${encodeURIComponent(repoSlugValue)}/pipelines/`,\n );\n u.searchParams.set('pagelen', String(PAGE_SIZE));\n u.searchParams.set('sort', '-created_on');\n if (options.since) {\n u.searchParams.set('q', `created_on >= ${options.since}`);\n }\n return u.toString();\n }\n\n private pullRequestsPath(repoSlugValue: string): string {\n return `/2.0/repositories/${this.settings.workspace}/${repoSlugValue}/pullrequests`;\n }\n\n private pipelinesPath(repoSlugValue: string): string {\n return `/2.0/repositories/${this.settings.workspace}/${repoSlugValue}/pipelines/`;\n }\n\n // -------------------------------------------------------------------------\n // Fetchers\n // -------------------------------------------------------------------------\n\n private async fetchPullRequestsPage(\n options: SyncOptions,\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const repos = this.settings.repoSlugs;\n if (repos.length === 0) {\n return { items: [], next: null };\n }\n const { idx, url: rawPageUrl } = decodePage(page);\n if (idx >= repos.length) {\n return { items: [], next: null };\n }\n const slug = repos[idx]!;\n const expectedPath = this.pullRequestsPath(slug);\n const fetchUrl =\n this.sanitizeUrl(rawPageUrl, expectedPath) ??\n this.buildPullRequestsUrl(slug, options);\n const res = await this.fetch<BitbucketPullRequestsResponse>(\n fetchUrl,\n 'pull_requests',\n signal,\n );\n const rows = res.body.values;\n const cutoff = options.since ? parseEpoch(options.since, 'iso') : null;\n let filtered: BitbucketPullRequest[];\n let cutoffReached: boolean;\n if (cutoff !== null) {\n filtered = rows.filter((pr) => {\n const ts = parseEpoch(pr.updated_on, 'iso');\n return ts === null || ts >= cutoff;\n });\n const last = rows.at(-1);\n const lastTs = last ? parseEpoch(last.updated_on, 'iso') : null;\n cutoffReached = lastTs !== null && lastTs < cutoff;\n } else {\n filtered = rows;\n cutoffReached = false;\n }\n const safeNext = this.sanitizeUrl(res.body.next ?? null, expectedPath);\n const nextWithinRepo = cutoffReached ? null : safeNext;\n const batch: RepoBatch<BitbucketPullRequest> = {\n repoSlug: slug,\n items: filtered,\n };\n if (nextWithinRepo !== null) {\n return { items: [batch], next: encodePage(idx, nextWithinRepo) };\n }\n const nextIdx = idx + 1;\n const next = nextIdx < repos.length ? encodePage(nextIdx, null) : null;\n return { items: [batch], next };\n }\n\n private async fetchPipelinesPage(\n options: SyncOptions,\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const repos = this.settings.repoSlugs;\n if (repos.length === 0) {\n return { items: [], next: null };\n }\n const { idx, url: rawPageUrl } = decodePage(page);\n if (idx >= repos.length) {\n return { items: [], next: null };\n }\n const slug = repos[idx]!;\n const expectedPath = this.pipelinesPath(slug);\n const fetchUrl =\n this.sanitizeUrl(rawPageUrl, expectedPath) ??\n this.buildPipelinesUrl(slug, options);\n const res = await this.fetch<BitbucketPipelinesResponse>(\n fetchUrl,\n 'pipelines',\n signal,\n );\n const rows = res.body.values;\n const cutoff = options.since ? parseEpoch(options.since, 'iso') : null;\n let filtered: BitbucketPipeline[];\n let cutoffReached: boolean;\n if (cutoff !== null) {\n filtered = rows.filter((p) => {\n const ts = parseEpoch(p.created_on, 'iso');\n return ts === null || ts >= cutoff;\n });\n const last = rows.at(-1);\n const lastTs = last ? parseEpoch(last.created_on, 'iso') : null;\n cutoffReached = lastTs !== null && lastTs < cutoff;\n } else {\n filtered = rows;\n cutoffReached = false;\n }\n const safeNext = this.sanitizeUrl(res.body.next ?? null, expectedPath);\n const nextWithinRepo = cutoffReached ? null : safeNext;\n const batch: RepoBatch<BitbucketPipeline> = {\n repoSlug: slug,\n items: filtered,\n };\n if (nextWithinRepo !== null) {\n return { items: [batch], next: encodePage(idx, nextWithinRepo) };\n }\n const nextIdx = idx + 1;\n const next = nextIdx < repos.length ? encodePage(nextIdx, null) : null;\n return { items: [batch], next };\n }\n\n // -------------------------------------------------------------------------\n // Writers\n // -------------------------------------------------------------------------\n\n private async writePullRequests(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n options: SyncOptions,\n ): Promise<void> {\n if (page === null && !options.since) {\n await storage.entities([], { types: ['pull_request'] });\n }\n const batches = items as RepoBatch<BitbucketPullRequest>[];\n for (const batch of batches) {\n for (const pr of batch.items) {\n const createdMs = parseEpoch(pr.created_on, 'iso');\n const updatedMs = parseEpoch(pr.updated_on, 'iso');\n if (createdMs === null || updatedMs === null) {\n continue;\n }\n await storage.entity({\n type: 'pull_request',\n id: `${this.settings.workspace}/${batch.repoSlug}:${pr.id}`,\n attributes: {\n workspace: this.settings.workspace,\n repo_slug: batch.repoSlug,\n pull_request_id: pr.id,\n title: pr.title,\n state: pr.state,\n author: pr.author?.nickname ?? pr.author?.display_name ?? null,\n source_branch: pr.source?.branch?.name ?? null,\n destination_branch: pr.destination?.branch?.name ?? null,\n web_url: pr.links?.html?.href ?? null,\n created_at: createdMs,\n closed_at: parseEpoch(pr.closed_on ?? null, 'iso'),\n },\n updated_at: updatedMs,\n });\n }\n }\n }\n\n private async writePipelines(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n options: SyncOptions,\n seenPipelineIds: Set<string>,\n ): Promise<void> {\n const pipelineAllowed = this.isResourceAllowed(\n 'pipeline',\n options.resources,\n );\n const eventAllowed = this.isResourceAllowed(\n 'pipeline_event',\n options.resources,\n );\n if (page === null && !options.since) {\n if (pipelineAllowed) {\n await storage.entities([], { types: ['pipeline'] });\n }\n if (eventAllowed) {\n await storage.events([], { names: ['pipeline_event'] });\n }\n }\n const batches = items as RepoBatch<BitbucketPipeline>[];\n for (const batch of batches) {\n for (const pipeline of batch.items) {\n const createdMs = parseEpoch(pipeline.created_on, 'iso');\n if (createdMs === null) {\n continue;\n }\n const entityId = `${this.settings.workspace}/${batch.repoSlug}:${pipeline.uuid}`;\n if (seenPipelineIds.has(entityId)) {\n continue;\n }\n seenPipelineIds.add(entityId);\n const completedMs = parseEpoch(pipeline.completed_on ?? null, 'iso');\n const durationMs =\n pipeline.duration_in_seconds !== null &&\n pipeline.duration_in_seconds !== undefined\n ? Math.round(pipeline.duration_in_seconds * 1000)\n : completedMs !== null\n ? completedMs - createdMs\n : null;\n const result = pipeline.state.result?.name ?? null;\n const refName = pipeline.target?.ref_name ?? null;\n const commitHash = pipeline.target?.commit?.hash ?? null;\n const triggerType = pipeline.trigger?.type ?? null;\n if (pipelineAllowed) {\n await storage.entity({\n type: 'pipeline',\n id: entityId,\n attributes: {\n workspace: this.settings.workspace,\n repo_slug: batch.repoSlug,\n uuid: pipeline.uuid,\n build_number: pipeline.build_number,\n state: pipeline.state.name,\n result,\n ref_name: refName,\n commit: commitHash,\n trigger_type: triggerType,\n creator:\n pipeline.creator?.nickname ??\n pipeline.creator?.display_name ??\n null,\n created_at: createdMs,\n completed_at: completedMs,\n duration_ms: durationMs,\n },\n updated_at: completedMs ?? createdMs,\n });\n }\n if (eventAllowed) {\n await storage.event({\n name: 'pipeline_event',\n start_ts: createdMs,\n end_ts: completedMs,\n attributes: {\n workspace: this.settings.workspace,\n repo_slug: batch.repoSlug,\n uuid: pipeline.uuid,\n build_number: pipeline.build_number,\n state: pipeline.state.name,\n result,\n ref_name: refName,\n commit: commitHash,\n trigger_type: triggerType,\n duration_ms: durationMs,\n },\n });\n }\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Cursor resume\n // -------------------------------------------------------------------------\n\n private resolveCursor(cursor: unknown): BitbucketSyncCursor | undefined {\n if (!isBitbucketSyncCursor(cursor)) {\n return undefined;\n }\n return { phase: cursor.phase, page: cursor.page };\n }\n\n // -------------------------------------------------------------------------\n // sync()\n // -------------------------------------------------------------------------\n\n async sync(\n options: SyncOptions,\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<SyncResult> {\n const cursor = this.resolveCursor(options.cursor);\n const phases = this.activePhases(options.resources);\n const seenPipelineIds = new Set<string>();\n return paginateChunked<BitbucketPhase, string>({\n phases,\n cursor,\n signal,\n logger: this.logger,\n fetchPage: async (phase, page, sig) => {\n switch (phase) {\n case 'pull_requests':\n return this.fetchPullRequestsPage(options, page, sig);\n case 'pipelines':\n return this.fetchPipelinesPage(options, page, sig);\n }\n },\n writeBatch: async (phase, items, page) => {\n switch (phase) {\n case 'pull_requests':\n return this.writePullRequests(storage, items, page, options);\n case 'pipelines':\n return this.writePipelines(\n storage,\n items,\n page,\n options,\n seenPipelineIds,\n );\n }\n },\n });\n }\n}\n","import { BitbucketConnector } from './bitbucket';\n\nexport {\n bitbucketResources as resources,\n BitbucketConnector,\n configFields,\n doc,\n id,\n} from './bitbucket';\nexport type { BitbucketResource, BitbucketSettings } from './bitbucket';\nexport default BitbucketConnector;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../connector-shared/src/errors.ts","../../../connector-shared/src/retry.ts","../../../connector-shared/src/version.ts","../../../connector-shared/src/request.ts","../../../connector-shared/src/rate-limit.ts","../../../connector-shared/src/map-concurrent.ts","../../../connector-shared/src/sanitize.ts","../../../connector-shared/src/epoch.ts","../../../connector-shared/src/pagination.ts","../../../connector-shared/src/logger.ts","../src/bitbucket.ts","../src/index.ts"],"sourcesContent":["import type { HttpResponse } from './types';\n\nexport type HttpErrorKind =\n | 'transient'\n | 'rate_limit'\n | 'auth'\n | 'upstream_bug'\n | 'client_bug';\n\nexport abstract class HttpClientError extends Error {\n abstract readonly kind: HttpErrorKind;\n readonly response?: HttpResponse;\n\n constructor(message: string, response?: HttpResponse) {\n super(message);\n this.name = new.target.name;\n this.response = response;\n }\n}\n\nexport class TransientError extends HttpClientError {\n readonly kind = 'transient' as const;\n}\n\nexport class RateLimitError extends HttpClientError {\n readonly kind = 'rate_limit' as const;\n readonly retryAfter?: Date;\n\n constructor(message: string, response?: HttpResponse, retryAfter?: Date) {\n super(message, response);\n this.retryAfter = retryAfter;\n }\n}\n\nexport class AuthError extends HttpClientError {\n readonly kind = 'auth' as const;\n}\n\nexport class UpstreamBugError extends HttpClientError {\n readonly kind = 'upstream_bug' as const;\n}\n\nexport class ClientBugError extends HttpClientError {\n readonly kind = 'client_bug' as const;\n}\n\nexport function classifyStatus(status: number): HttpErrorKind {\n if (status === 429) {\n return 'rate_limit';\n }\n if (status === 401 || status === 403) {\n return 'auth';\n }\n if (status === 408) {\n return 'transient';\n }\n if (status >= 500) {\n return 'upstream_bug';\n }\n if (status >= 400) {\n return 'client_bug';\n }\n return 'client_bug';\n}\n\nexport function errorForStatus(\n message: string,\n response: HttpResponse,\n retryAfter?: Date,\n): HttpClientError {\n const kind = classifyStatus(response.status);\n switch (kind) {\n case 'rate_limit':\n return new RateLimitError(message, response, retryAfter);\n case 'auth':\n return new AuthError(message, response);\n case 'transient':\n return new TransientError(message, response);\n case 'upstream_bug':\n return new UpstreamBugError(message, response);\n case 'client_bug':\n return new ClientBugError(message, response);\n }\n}\n","import { HttpClientError, RateLimitError, TransientError } from './errors';\n\nexport interface RetryPolicy {\n maxAttempts?: number;\n initialDelayMs?: number;\n maxDelayMs?: number;\n retryOn?: (status: number | null, err?: Error) => boolean;\n}\n\nexport const defaultRetryOn = (status: number | null, err?: Error): boolean => {\n if (err instanceof RateLimitError) {\n return true;\n }\n if (err instanceof TransientError) {\n return true;\n }\n if (status === null) {\n return err instanceof Error && !(err instanceof HttpClientError);\n }\n if (status === 408 || status === 429) {\n return true;\n }\n if (status >= 500) {\n return true;\n }\n return false;\n};\n\nexport function backoffDelayMs(\n attempt: number,\n policy: Required<Pick<RetryPolicy, 'initialDelayMs' | 'maxDelayMs'>>,\n): number {\n const base = policy.initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, policy.maxDelayMs);\n}\n\nexport function parseRetryAfter(\n headerValue: string | null,\n now: Date = new Date(),\n): Date | undefined {\n if (!headerValue) {\n return undefined;\n }\n const trimmed = headerValue.trim();\n if (/^\\d+$/.test(trimmed)) {\n return new Date(now.getTime() + Number(trimmed) * 1000);\n }\n const parsed = Date.parse(trimmed);\n if (Number.isNaN(parsed)) {\n return undefined;\n }\n return new Date(parsed);\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.reject(signal.reason ?? new Error('Aborted'));\n }\n return new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal!.reason ?? new Error('Aborted'));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n","export const HTTP_CLIENT_VERSION = '0.0.0';\n\nexport const DEFAULT_USER_AGENT = `rawdash-connector/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n\nexport function connectorUserAgent(connectorId: string): string {\n return `rawdash-connector-${connectorId}/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n}\n","import {\n AuthError,\n ClientBugError,\n HttpClientError,\n RateLimitError,\n TransientError,\n UpstreamBugError,\n errorForStatus,\n} from './errors';\nimport { defaultRetryOn, parseRetryAfter, sleep } from './retry';\nimport type { FetchLike, HttpMethod, HttpRequest, HttpResponse } from './types';\nimport { DEFAULT_USER_AGENT } from './version';\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_ATTEMPTS = 3;\nconst DEFAULT_INITIAL_DELAY_MS = 1000;\nconst DEFAULT_MAX_DELAY_MS = 60_000;\nconst OBSERVER_TIMEOUT_MS = 250;\n\nexport interface RequestObservation {\n url: string;\n method: HttpMethod;\n status: number;\n resource: string;\n requestId: string;\n body: unknown;\n}\n\nexport type RequestObserver = (\n event: RequestObservation,\n) => void | Promise<void>;\n\nexport interface RequestOptions {\n fetch?: FetchLike;\n observer?: RequestObserver;\n resource: string;\n requestId?: string;\n}\n\nasync function notifyObserver(\n observer: RequestObserver,\n event: RequestObservation,\n): Promise<void> {\n let result: void | Promise<void>;\n try {\n result = observer(event);\n } catch (err) {\n console.warn('[connector-shared] request observer threw:', err);\n return;\n }\n if (!(result instanceof Promise)) {\n return;\n }\n const guarded = result.catch((err) => {\n console.warn('[connector-shared] request observer rejected:', err);\n });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, OBSERVER_TIMEOUT_MS);\n });\n try {\n await Promise.race([guarded, timeout]);\n } finally {\n if (timer) {\n clearTimeout(timer);\n }\n }\n}\n\nfunction newRequestId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) {\n return c.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction mergeHeaders(\n defaults: Record<string, string>,\n overrides: Record<string, string> | undefined,\n): Record<string, string> {\n const merged: Record<string, string> = {};\n for (const [k, v] of Object.entries(defaults)) {\n merged[k.toLowerCase()] = v;\n }\n if (overrides) {\n for (const [k, v] of Object.entries(overrides)) {\n merged[k.toLowerCase()] = v;\n }\n }\n return merged;\n}\n\nfunction linkTimeoutSignal(\n parent: AbortSignal | undefined,\n timeoutMs: number,\n): { signal: AbortSignal; cancel: () => void } {\n const controller = new AbortController();\n const onParentAbort = () => {\n controller.abort(parent?.reason);\n };\n if (parent) {\n if (parent.aborted) {\n controller.abort(parent.reason);\n } else {\n parent.addEventListener('abort', onParentAbort, { once: true });\n }\n }\n const timer = setTimeout(() => {\n controller.abort(new Error(`Request timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n return {\n signal: controller.signal,\n cancel: () => {\n clearTimeout(timer);\n if (parent) {\n parent.removeEventListener('abort', onParentAbort);\n }\n },\n };\n}\n\nasync function readBody(res: Response, parseJson: boolean): Promise<unknown> {\n if (res.status === 204 || res.status === 205) {\n return null;\n }\n const contentType = res.headers.get('content-type') ?? '';\n if (parseJson && contentType.includes('application/json')) {\n const text = await res.text();\n if (text.length === 0) {\n return null;\n }\n return JSON.parse(text);\n }\n return res.text();\n}\n\nexport async function request<T = unknown>(\n req: HttpRequest,\n options: RequestOptions,\n): Promise<HttpResponse<T>> {\n const fetchImpl: FetchLike = options.fetch ?? (globalThis.fetch as FetchLike);\n const retry = req.retry ?? {};\n const maxAttempts = retry.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;\n const initialDelayMs = retry.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;\n const maxDelayMs = retry.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;\n const retryOn = retry.retryOn ?? defaultRetryOn;\n const timeoutMs = req.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const parseJson = req.parseJson ?? true;\n\n const headers = mergeHeaders(\n {\n 'User-Agent': DEFAULT_USER_AGENT,\n Accept: 'application/json',\n },\n req.headers,\n );\n\n let lastErr: Error | undefined;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n req.signal?.throwIfAborted();\n\n const { signal, cancel } = linkTimeoutSignal(req.signal, timeoutMs);\n let res: Response;\n try {\n res = await fetchImpl(req.url, {\n method: req.method ?? 'GET',\n headers,\n body: req.body as RequestInit['body'],\n signal,\n });\n } catch (err) {\n cancel();\n if (req.signal?.aborted) {\n throw req.signal.reason ?? err;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n lastErr = error;\n if (attempt < maxAttempts - 1 && retryOn(null, error)) {\n const delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n await sleep(delay, req.signal);\n continue;\n }\n throw new TransientError(error.message);\n }\n cancel();\n\n const body = await readBody(res, parseJson);\n const httpResponse: HttpResponse<T> = {\n status: res.status,\n headers: res.headers,\n body: body as T,\n };\n if (req.rateLimit) {\n const state = req.rateLimit.parse(res.headers);\n if (state) {\n httpResponse.rateLimitState = state;\n }\n }\n\n if (options.observer) {\n await notifyObserver(options.observer, {\n url: req.url,\n method: req.method ?? 'GET',\n status: res.status,\n resource: options.resource,\n requestId: options.requestId ?? newRequestId(),\n body,\n });\n }\n\n if (res.ok) {\n return httpResponse;\n }\n\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\n const message = `HTTP ${res.status} ${res.statusText} for ${req.method ?? 'GET'} ${req.url}`;\n const err = errorForStatus(message, httpResponse, retryAfter);\n\n if (\n attempt < maxAttempts - 1 &&\n retryOn(res.status, err) &&\n !(err instanceof AuthError) &&\n !(err instanceof ClientBugError)\n ) {\n lastErr = err;\n let delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n if (err instanceof RateLimitError && retryAfter) {\n const wait = retryAfter.getTime() - Date.now();\n if (wait > 0) {\n delay = Math.min(wait, maxDelayMs);\n }\n }\n await sleep(delay, req.signal);\n continue;\n }\n\n throw err;\n }\n\n throw lastErr ?? new UpstreamBugError('Exhausted retry attempts');\n}\n\nfunction computeDelay(\n attempt: number,\n initialDelayMs: number,\n maxDelayMs: number,\n): number {\n const base = initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, maxDelayMs);\n}\n\nexport { HttpClientError };\n","export interface RateLimitState {\n remaining: number;\n resetAt: Date;\n}\n\nexport interface RateLimitPolicy {\n parse(headers: Headers): RateLimitState | null;\n}\n\nexport interface StandardRateLimitPolicyConfig {\n remainingHeader: string;\n resetHeader: string;\n resetUnit: 's' | 'ms';\n resetFallbackMs?: number;\n}\n\nexport function standardRateLimitPolicy(\n config: StandardRateLimitPolicyConfig,\n): RateLimitPolicy {\n const { remainingHeader, resetHeader, resetUnit, resetFallbackMs } = config;\n const multiplier = resetUnit === 's' ? 1000 : 1;\n return {\n parse(h) {\n const remainingRaw = h.get(remainingHeader);\n if (remainingRaw === null || remainingRaw.trim() === '') {\n return null;\n }\n const remaining = Number(remainingRaw);\n if (!Number.isFinite(remaining)) {\n return null;\n }\n const resetRaw = h.get(resetHeader);\n if (resetRaw === null) {\n if (resetFallbackMs === undefined) {\n return null;\n }\n return {\n remaining,\n resetAt: new Date(Date.now() + resetFallbackMs),\n };\n }\n if (resetRaw.trim() === '') {\n return null;\n }\n const reset = Number(resetRaw);\n if (!Number.isFinite(reset) || reset < 0) {\n return null;\n }\n const resetMs = reset * multiplier;\n if (!Number.isFinite(resetMs)) {\n return null;\n }\n return { remaining, resetAt: new Date(resetMs) };\n },\n };\n}\n","export async function mapWithConcurrency<T, R>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n if (items.length === 0) {\n return results;\n }\n const normalized = Number.isFinite(concurrency) ? Math.floor(concurrency) : 1;\n const limit = Math.max(1, Math.min(normalized, items.length));\n let next = 0;\n let failed = false;\n\n async function worker(): Promise<void> {\n while (!failed) {\n const i = next++;\n if (i >= items.length) {\n return;\n }\n try {\n results[i] = await fn(items[i]!, i);\n } catch (err) {\n failed = true;\n throw err;\n }\n }\n }\n\n const workers: Promise<void>[] = [];\n for (let w = 0; w < limit; w++) {\n workers.push(worker());\n }\n await Promise.all(workers);\n return results;\n}\n","export interface SanitizeAllowedUrlOptions {\n url: string | null;\n host: string;\n pathname: string;\n protocol?: 'https:' | 'http:';\n}\n\nexport function sanitizeAllowedUrl(\n options: SanitizeAllowedUrlOptions,\n): string | null {\n const { url, host, pathname, protocol = 'https:' } = options;\n if (url === null) {\n return null;\n }\n try {\n const u = new URL(url);\n if (u.protocol !== protocol || u.host !== host || u.pathname !== pathname) {\n return null;\n }\n return u.toString();\n } catch {\n return null;\n }\n}\n","export type EpochUnit = 'ms' | 's' | 'iso';\n\nexport function parseEpoch(\n value: number | string | null | undefined,\n unit: EpochUnit,\n): number | null {\n if (value === null || value === undefined) {\n return null;\n }\n if (unit === 'iso') {\n if (typeof value !== 'string') {\n return null;\n }\n const ms = new Date(value).getTime();\n return Number.isFinite(ms) ? ms : null;\n }\n if (typeof value === 'string' && value.trim() === '') {\n return null;\n }\n const n = typeof value === 'number' ? value : Number(value);\n if (!Number.isFinite(n)) {\n return null;\n }\n const result = unit === 's' ? n * 1000 : n;\n return Number.isFinite(result) ? result : null;\n}\n","import { request } from './request';\nimport type { HttpRequest } from './types';\n\nexport function parseLinkHeader(header: string | null): Record<string, string> {\n if (!header) {\n return {};\n }\n const result: Record<string, string> = {};\n for (const part of header.split(',')) {\n const match = part.match(/<([^>]+)>\\s*;\\s*rel=\"([^\"]+)\"/);\n if (match) {\n result[match[2]!] = match[1]!;\n }\n }\n return result;\n}\n\nexport async function* paginateLink<T>(\n initial: HttpRequest,\n parse: (body: unknown) => T[],\n options: { resource: string },\n): AsyncIterable<T> {\n let next: string | null = initial.url;\n while (next) {\n const res: Awaited<ReturnType<typeof request>> = await request(\n {\n ...initial,\n url: next,\n },\n { resource: options.resource },\n );\n for (const item of parse(res.body)) {\n yield item;\n }\n const links = parseLinkHeader(res.headers.get('link'));\n next = links['next'] ?? null;\n }\n}\n\nexport async function* paginateCursor<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; nextCursor: string | null },\n buildNext: (req: HttpRequest, cursor: string) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let req: HttpRequest = initial;\n while (true) {\n const res = await request(req, { resource: options.resource });\n const { items, nextCursor } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!nextCursor) {\n return;\n }\n req = buildNext(req, nextCursor);\n }\n}\n\nexport async function* paginatePage<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; hasMore: boolean },\n buildPage: (req: HttpRequest, page: number) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let page = 1;\n while (true) {\n const req = page === 1 ? initial : buildPage(initial, page);\n const res = await request(req, { resource: options.resource });\n const { items, hasMore } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!hasMore || items.length === 0) {\n return;\n }\n page++;\n }\n}\n","export type LogFields = Record<string, unknown>;\n\nexport interface ConnectorLogger {\n info(event: string, fields?: LogFields): void;\n warn(event: string, fields?: LogFields): void;\n}\n\nexport interface ConnectorLoggerOptions {\n scope: string;\n}\n\nconst MAX_VALUE_LEN = 120;\n\nfunction truncate(s: string, max = MAX_VALUE_LEN): string {\n if (s.length <= max) {\n return s;\n }\n return `${s.slice(0, max - 1)}…`;\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return '';\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (typeof value === 'string') {\n const t = truncate(value);\n if (/[\\s\"=]/.test(t)) {\n return JSON.stringify(t);\n }\n return t;\n }\n if (typeof value === 'bigint') {\n return value.toString();\n }\n let json: string | undefined;\n try {\n json = JSON.stringify(value);\n } catch {\n json = undefined;\n }\n return truncate(json ?? String(value));\n}\n\nexport function formatLogFields(fields?: LogFields): string {\n if (!fields) {\n return '';\n }\n const parts: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (v === undefined) {\n continue;\n }\n parts.push(`${k}=${formatValue(v)}`);\n }\n return parts.length > 0 ? ` ${parts.join(' ')}` : '';\n}\n\nexport function formatLogLine(\n scope: string,\n event: string,\n fields?: LogFields,\n): string {\n return `[${scope}] ${event}${formatLogFields(fields)}`;\n}\n\nexport function createDefaultConnectorLogger(\n opts: ConnectorLoggerOptions,\n): ConnectorLogger {\n return {\n info(event, fields) {\n console.info(formatLogLine(opts.scope, event, fields));\n },\n warn(event, fields) {\n console.warn(formatLogLine(opts.scope, event, fields));\n },\n };\n}\n\nconst NOOP_LOGGER: ConnectorLogger = {\n info() {},\n warn() {},\n};\n\nexport function noopConnectorLogger(): ConnectorLogger {\n return NOOP_LOGGER;\n}\n","import {\n type HttpResponse,\n connectorUserAgent,\n parseEpoch,\n} from '@rawdash/connector-shared';\nimport {\n BaseConnector,\n type ChunkedSyncCursor,\n type ConnectorContext,\n type ConnectorDoc,\n type CredentialsSchema,\n type FetchPageResult,\n type FetchSpec,\n type FilterClause,\n type StorageHandle,\n type SyncOptions,\n type SyncResult,\n defineConfigFields,\n defineConnectorDoc,\n defineResources,\n makeChunkedCursorGuard,\n paginateChunked,\n schemasFromResources,\n selectActivePhases,\n} from '@rawdash/core';\nimport { z } from 'zod';\n\n// ---------------------------------------------------------------------------\n// configFields\n// ---------------------------------------------------------------------------\n\nconst repoSlug = z\n .string()\n .min(1)\n .regex(\n /^[^/\\s?#]+$/,\n 'Use the repository slug only (no workspace prefix, slashes, or query).',\n );\n\nexport const configFields = defineConfigFields(\n z.object({\n workspace: z\n .string()\n .min(1)\n .regex(\n /^[^/\\s?#]+$/,\n 'Use the workspace slug only (no slashes, spaces, or query).',\n )\n .meta({\n label: 'Workspace',\n description:\n 'Bitbucket Cloud workspace slug (the segment shown in repo URLs after bitbucket.org/).',\n placeholder: 'my-workspace',\n }),\n username: z.string().min(1).meta({\n label: 'Atlassian username',\n description:\n 'Atlassian account username paired with the app password for Basic auth (find it under Personal settings -> Account settings).',\n placeholder: 'janedoe',\n }),\n appPassword: z.object({ $secret: z.string() }).meta({\n label: 'App password',\n description:\n 'Bitbucket app password with `Repositories:Read` and `Pipelines:Read` scopes. Create one at Personal settings -> App passwords.',\n placeholder: 'ATBB...',\n secret: true,\n }),\n repoSlugs: z\n .array(repoSlug)\n .nonempty()\n .refine((slugs) => new Set(slugs).size === slugs.length, {\n error: 'Repository slugs must be unique.',\n })\n .meta({\n label: 'Repository slugs',\n description:\n 'Repositories to sync, named by their slug within the workspace (no `workspace/` prefix).',\n }),\n resources: z\n .array(z.enum(['pull_request', 'pipeline', 'pipeline_event']))\n .nonempty()\n .optional()\n .meta({\n label: 'Resources',\n description:\n \"Which Bitbucket resources to sync. Omit to sync all of them. 'pipeline_event' rides the 'pipeline' phase - enabling it without 'pipeline' still fetches pipelines but skips writing pipeline entities.\",\n }),\n }),\n);\n\n// ---------------------------------------------------------------------------\n// Connector documentation metadata\n// ---------------------------------------------------------------------------\n\nexport const doc: ConnectorDoc = defineConnectorDoc({\n displayName: 'Bitbucket',\n category: 'engineering',\n brandColor: '#0052CC',\n tagline:\n 'Sync pull requests, pipelines, and pipeline lifecycle events from Bitbucket Cloud repositories.',\n vendor: {\n name: 'Atlassian',\n domain: 'bitbucket.org',\n apiDocs: 'https://developer.atlassian.com/cloud/bitbucket/rest/intro/',\n website: 'https://bitbucket.org',\n },\n auth: {\n summary:\n 'Authenticates over HTTP Basic auth using an Atlassian account username and a Bitbucket app password. The password is scoped to the projects and repositories the account can already read.',\n setup: [\n 'Open Bitbucket -> Personal settings -> App passwords (https://bitbucket.org/account/settings/app-passwords/).',\n 'Create an app password with `Repositories:Read` and `Pipelines:Read` scopes.',\n 'Store it as a secret and reference it from the connector config as `appPassword: secret(\"BITBUCKET_APP_PASSWORD\")`, alongside your `workspace`, `username`, and the list of `repoSlugs` to sync.',\n ],\n },\n rateLimit:\n 'Bitbucket Cloud applies hourly per-IP and per-user limits (around 1,000 requests/hour for app-password auth). Pagination uses a `next` URL in each response and a configurable `pagelen` (capped at 50 here).',\n limitations: [\n 'Bitbucket Server / Data Center are out of scope; this connector targets Bitbucket Cloud only.',\n 'Pipeline state-transition events are synthesized: one `pipeline_event` is emitted per pipeline lifecycle (created_on to completed_on/updated_on), not one per intermediate state change.',\n 'Repository discovery is not automatic - configure each repository slug explicitly via `repoSlugs`.',\n ],\n});\n\n// ---------------------------------------------------------------------------\n// Settings and credentials\n// ---------------------------------------------------------------------------\n\nexport type BitbucketResource = 'pull_request' | 'pipeline' | 'pipeline_event';\n\nexport interface BitbucketSettings {\n workspace: string;\n repoSlugs: readonly string[];\n resources?: readonly BitbucketResource[];\n}\n\nconst bitbucketCredentials = {\n username: {\n description: 'Atlassian account username',\n auth: 'required' as const,\n },\n appPassword: {\n description: 'Bitbucket app password',\n auth: 'required' as const,\n },\n} satisfies CredentialsSchema;\n\ntype BitbucketCredentials = typeof bitbucketCredentials;\n\nconst API_HOST = 'api.bitbucket.org';\nconst API_BASE = `https://${API_HOST}`;\nconst PAGE_SIZE = 50;\n\n// ---------------------------------------------------------------------------\n// Sync phases + cursor\n// ---------------------------------------------------------------------------\n\nconst PHASE_ORDER = ['pull_requests', 'pipelines'] as const;\n\ntype BitbucketPhase = (typeof PHASE_ORDER)[number];\n\ntype BitbucketSyncCursor = ChunkedSyncCursor<BitbucketPhase, string>;\n\nconst isBitbucketSyncCursor = makeChunkedCursorGuard(PHASE_ORDER);\n\n// Page-cursor encoding: `<repoIdx>|<pageUrl?>`.\n// - null page -> start at repoIdx=0 with no URL yet\n// - \"<idx>|\" -> start of repo at idx, build initial URL\n// - \"<idx>|<url>\" -> continuing pagination for repo at idx\nfunction decodePage(page: string | null): {\n idx: number;\n url: string | null;\n} {\n if (page === null) {\n return { idx: 0, url: null };\n }\n const sep = page.indexOf('|');\n if (sep === -1) {\n return { idx: 0, url: null };\n }\n const idxRaw = Number.parseInt(page.slice(0, sep), 10);\n const url = page.slice(sep + 1);\n return {\n idx: Number.isFinite(idxRaw) && idxRaw >= 0 ? idxRaw : 0,\n url: url === '' ? null : url,\n };\n}\n\nfunction encodePage(idx: number, url: string | null): string {\n return `${idx}|${url ?? ''}`;\n}\n\n// ---------------------------------------------------------------------------\n// Bitbucket API types\n// ---------------------------------------------------------------------------\n\ninterface BitbucketAccountRef {\n uuid?: string | null;\n display_name?: string | null;\n nickname?: string | null;\n account_id?: string | null;\n}\n\ninterface BitbucketBranchRef {\n branch?: { name?: string | null } | null;\n commit?: { hash?: string | null } | null;\n}\n\ninterface BitbucketPullRequest {\n id: number;\n title: string;\n state: string;\n author?: BitbucketAccountRef | null;\n source?: BitbucketBranchRef | null;\n destination?: BitbucketBranchRef | null;\n created_on: string;\n updated_on: string;\n closed_on?: string | null;\n links?: { html?: { href?: string | null } | null } | null;\n}\n\ninterface BitbucketPullRequestsResponse {\n values: BitbucketPullRequest[];\n next?: string | null;\n page?: number | null;\n pagelen?: number | null;\n}\n\ninterface BitbucketPipelineTarget {\n ref_name?: string | null;\n commit?: { hash?: string | null } | null;\n selector?: { type?: string | null; pattern?: string | null } | null;\n}\n\ninterface BitbucketPipelineState {\n name: string;\n type?: string | null;\n result?: { name?: string | null } | null;\n}\n\ninterface BitbucketPipeline {\n uuid: string;\n build_number: number;\n state: BitbucketPipelineState;\n creator?: BitbucketAccountRef | null;\n target?: BitbucketPipelineTarget | null;\n trigger?: { name?: string | null; type?: string | null } | null;\n created_on: string;\n completed_on?: string | null;\n duration_in_seconds?: number | null;\n build_seconds_used?: number | null;\n}\n\ninterface BitbucketPipelinesResponse {\n values: BitbucketPipeline[];\n next?: string | null;\n page?: number | null;\n pagelen?: number | null;\n}\n\n// ---------------------------------------------------------------------------\n// Zod response schemas\n// ---------------------------------------------------------------------------\n\nconst accountRefSchema = z.object({\n uuid: z.string().nullable().optional(),\n display_name: z.string().nullable().optional(),\n nickname: z.string().nullable().optional(),\n account_id: z.string().nullable().optional(),\n});\n\nconst branchRefSchema = z.object({\n branch: z\n .object({ name: z.string().nullable().optional() })\n .nullable()\n .optional(),\n commit: z\n .object({ hash: z.string().nullable().optional() })\n .nullable()\n .optional(),\n});\n\nconst pullRequestSchema = z.object({\n id: z.number().int().nonnegative(),\n title: z.string(),\n state: z.string().min(1),\n author: accountRefSchema.nullable().optional(),\n source: branchRefSchema.nullable().optional(),\n destination: branchRefSchema.nullable().optional(),\n created_on: z.iso.datetime(),\n updated_on: z.iso.datetime(),\n closed_on: z.iso.datetime().nullable().optional(),\n links: z\n .object({\n html: z\n .object({ href: z.string().nullable().optional() })\n .nullable()\n .optional(),\n })\n .nullable()\n .optional(),\n});\n\nconst pullRequestsResponseSchema = z.object({\n values: z.array(pullRequestSchema),\n next: z.string().nullable().optional(),\n page: z.number().int().nullable().optional(),\n pagelen: z.number().int().nullable().optional(),\n});\n\nconst pipelineStateSchema = z.object({\n name: z.string().min(1),\n type: z.string().nullable().optional(),\n result: z\n .object({ name: z.string().nullable().optional() })\n .nullable()\n .optional(),\n});\n\nconst pipelineTargetSchema = z.object({\n ref_name: z.string().nullable().optional(),\n commit: z\n .object({ hash: z.string().nullable().optional() })\n .nullable()\n .optional(),\n selector: z\n .object({\n type: z.string().nullable().optional(),\n pattern: z.string().nullable().optional(),\n })\n .nullable()\n .optional(),\n});\n\nconst pipelineSchema = z.object({\n uuid: z.string().min(1),\n build_number: z.number().int().nonnegative(),\n state: pipelineStateSchema,\n creator: accountRefSchema.nullable().optional(),\n target: pipelineTargetSchema.nullable().optional(),\n trigger: z\n .object({\n name: z.string().nullable().optional(),\n type: z.string().nullable().optional(),\n })\n .nullable()\n .optional(),\n created_on: z.iso.datetime(),\n completed_on: z.iso.datetime().nullable().optional(),\n duration_in_seconds: z.number().nullable().optional(),\n build_seconds_used: z.number().nullable().optional(),\n});\n\nconst pipelinesResponseSchema = z.object({\n values: z.array(pipelineSchema),\n next: z.string().nullable().optional(),\n page: z.number().int().nullable().optional(),\n pagelen: z.number().int().nullable().optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Resource definitions\n// ---------------------------------------------------------------------------\n\nexport const bitbucketResources = defineResources({\n pull_request: {\n shape: 'entity',\n description:\n 'Open, merged, declined, and superseded pull requests with author, source/target branches, and close timestamp.',\n endpoint:\n 'GET /2.0/repositories/{workspace}/{repo_slug}/pullrequests?state=OPEN,MERGED,DECLINED,SUPERSEDED',\n notes:\n 'Paginated newest-first by `updated_on`; the connector stops once a page is entirely older than `options.since`.',\n filterable: [\n {\n field: 'state',\n ops: ['eq'],\n values: ['OPEN', 'MERGED', 'DECLINED', 'SUPERSEDED'],\n },\n ],\n responses: { pull_requests: pullRequestsResponseSchema },\n },\n pipeline: {\n shape: 'entity',\n description:\n 'Bitbucket Pipelines runs with state, result, target ref/commit, trigger, duration, and create/complete timestamps.',\n endpoint: 'GET /2.0/repositories/{workspace}/{repo_slug}/pipelines/',\n notes:\n 'Paginated newest-first by `created_on`; the connector stops once a page is entirely older than `options.since`.',\n filterable: [],\n responses: { pipelines: pipelinesResponseSchema },\n },\n pipeline_event: {\n shape: 'event',\n description:\n 'Pipeline lifecycle events. One event per pipeline covering created_on to completed_on (or updated_on if not yet finished), tagged with the terminal state and result.',\n endpoint: 'GET /2.0/repositories/{workspace}/{repo_slug}/pipelines/',\n notes:\n 'Derived from the same pipelines response that builds the `pipeline` resource; the Bitbucket API does not expose an intermediate state-transition history endpoint.',\n filterable: [],\n },\n});\n\nexport const id = 'bitbucket';\n\n// ---------------------------------------------------------------------------\n// Connector class\n// ---------------------------------------------------------------------------\n\ninterface RepoBatch<T> {\n repoSlug: string;\n items: T[];\n}\n\nconst PULL_REQUEST_STATES = new Set([\n 'OPEN',\n 'MERGED',\n 'DECLINED',\n 'SUPERSEDED',\n]);\n\nfunction pushablePullRequestState(\n filter: FilterClause[] | undefined,\n): string | null {\n if (!filter) {\n return null;\n }\n for (const clause of filter) {\n if (\n 'field' in clause &&\n clause.field === 'state' &&\n clause.op === 'eq' &&\n typeof clause.value === 'string' &&\n PULL_REQUEST_STATES.has(clause.value)\n ) {\n return clause.value;\n }\n }\n return null;\n}\n\nexport class BitbucketConnector extends BaseConnector<\n BitbucketSettings,\n BitbucketCredentials\n> {\n static readonly id = id;\n\n static readonly resources = bitbucketResources;\n\n static readonly schemas = schemasFromResources(bitbucketResources);\n\n static create(input: unknown, ctx?: ConnectorContext): BitbucketConnector {\n const parsed = configFields.parse(input);\n return new BitbucketConnector(\n {\n workspace: parsed.workspace,\n repoSlugs: parsed.repoSlugs,\n resources: parsed.resources,\n },\n { username: parsed.username, appPassword: parsed.appPassword },\n ctx,\n );\n }\n\n readonly id = id;\n override readonly credentials = bitbucketCredentials;\n\n private buildHeaders(): Record<string, string> {\n const basic = btoa(`${this.creds.username}:${this.creds.appPassword}`);\n return {\n Authorization: `Basic ${basic}`,\n Accept: 'application/json',\n 'User-Agent': connectorUserAgent('bitbucket'),\n };\n }\n\n private fetch<T>(\n url: string,\n resource: string,\n signal: AbortSignal | undefined,\n ): Promise<HttpResponse<T>> {\n return this.get<T>(url, {\n resource,\n headers: this.buildHeaders(),\n signal,\n });\n }\n\n private sanitizeUrl(url: string | null, expectedPath: string): string | null {\n if (!url) {\n return null;\n }\n try {\n const u = new URL(url);\n if (u.protocol !== 'https:' || u.host !== API_HOST) {\n return null;\n }\n if (u.pathname !== expectedPath) {\n return null;\n }\n return u.toString();\n } catch {\n return null;\n }\n }\n\n private static readonly PHASE_RESOURCES: Record<\n BitbucketPhase,\n readonly BitbucketResource[]\n > = {\n pull_requests: ['pull_request'],\n pipelines: ['pipeline', 'pipeline_event'],\n };\n\n private activePhases(\n optionsResources: ReadonlySet<string> | undefined,\n ): BitbucketPhase[] {\n const fromSettings = selectActivePhases<BitbucketResource, BitbucketPhase>(\n (r) => {\n switch (r) {\n case 'pull_request':\n return 'pull_requests';\n case 'pipeline':\n case 'pipeline_event':\n return 'pipelines';\n }\n },\n PHASE_ORDER,\n this.settings.resources,\n );\n if (optionsResources === undefined) {\n return fromSettings;\n }\n return fromSettings.filter((phase) =>\n BitbucketConnector.PHASE_RESOURCES[phase].some((r) =>\n optionsResources.has(r),\n ),\n );\n }\n\n private isResourceAllowed(\n resource: BitbucketResource,\n optionsResources: ReadonlySet<string> | undefined,\n ): boolean {\n const fromSettings = this.settings.resources;\n if (\n fromSettings &&\n fromSettings.length > 0 &&\n !fromSettings.includes(resource)\n ) {\n return false;\n }\n if (optionsResources !== undefined && !optionsResources.has(resource)) {\n return false;\n }\n return true;\n }\n\n // -------------------------------------------------------------------------\n // URL builders\n // -------------------------------------------------------------------------\n\n private buildPullRequestsUrl(\n repoSlugValue: string,\n options: SyncOptions,\n spec?: FetchSpec,\n ): string {\n const u = new URL(\n `${API_BASE}/2.0/repositories/${encodeURIComponent(this.settings.workspace)}/${encodeURIComponent(repoSlugValue)}/pullrequests`,\n );\n u.searchParams.set('pagelen', String(PAGE_SIZE));\n u.searchParams.set('sort', '-updated_on');\n u.searchParams.set(\n 'state',\n pushablePullRequestState(spec?.filter) ??\n 'OPEN,MERGED,DECLINED,SUPERSEDED',\n );\n if (options.since) {\n u.searchParams.set('q', `updated_on >= ${options.since}`);\n }\n return u.toString();\n }\n\n private singleSpec(\n options: SyncOptions,\n resource: string,\n ): FetchSpec | undefined {\n const specs = options.fetchSpecs?.[resource];\n return specs && specs.length === 1 ? specs[0] : undefined;\n }\n\n private buildPipelinesUrl(\n repoSlugValue: string,\n options: SyncOptions,\n ): string {\n const u = new URL(\n `${API_BASE}/2.0/repositories/${encodeURIComponent(this.settings.workspace)}/${encodeURIComponent(repoSlugValue)}/pipelines/`,\n );\n u.searchParams.set('pagelen', String(PAGE_SIZE));\n u.searchParams.set('sort', '-created_on');\n if (options.since) {\n u.searchParams.set('q', `created_on >= ${options.since}`);\n }\n return u.toString();\n }\n\n private pullRequestsPath(repoSlugValue: string): string {\n return `/2.0/repositories/${this.settings.workspace}/${repoSlugValue}/pullrequests`;\n }\n\n private pipelinesPath(repoSlugValue: string): string {\n return `/2.0/repositories/${this.settings.workspace}/${repoSlugValue}/pipelines/`;\n }\n\n // -------------------------------------------------------------------------\n // Fetchers\n // -------------------------------------------------------------------------\n\n private async fetchPullRequestsPage(\n options: SyncOptions,\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const repos = this.settings.repoSlugs;\n if (repos.length === 0) {\n return { items: [], next: null };\n }\n const { idx, url: rawPageUrl } = decodePage(page);\n if (idx >= repos.length) {\n return { items: [], next: null };\n }\n const slug = repos[idx]!;\n const expectedPath = this.pullRequestsPath(slug);\n const fetchUrl =\n this.sanitizeUrl(rawPageUrl, expectedPath) ??\n this.buildPullRequestsUrl(\n slug,\n options,\n this.singleSpec(options, 'pull_request'),\n );\n const res = await this.fetch<BitbucketPullRequestsResponse>(\n fetchUrl,\n 'pull_requests',\n signal,\n );\n const rows = res.body.values;\n const cutoff = options.since ? parseEpoch(options.since, 'iso') : null;\n let filtered: BitbucketPullRequest[];\n let cutoffReached: boolean;\n if (cutoff !== null) {\n filtered = rows.filter((pr) => {\n const ts = parseEpoch(pr.updated_on, 'iso');\n return ts === null || ts >= cutoff;\n });\n const last = rows.at(-1);\n const lastTs = last ? parseEpoch(last.updated_on, 'iso') : null;\n cutoffReached = lastTs !== null && lastTs < cutoff;\n } else {\n filtered = rows;\n cutoffReached = false;\n }\n const safeNext = this.sanitizeUrl(res.body.next ?? null, expectedPath);\n const nextWithinRepo = cutoffReached ? null : safeNext;\n const batch: RepoBatch<BitbucketPullRequest> = {\n repoSlug: slug,\n items: filtered,\n };\n if (nextWithinRepo !== null) {\n return { items: [batch], next: encodePage(idx, nextWithinRepo) };\n }\n const nextIdx = idx + 1;\n const next = nextIdx < repos.length ? encodePage(nextIdx, null) : null;\n return { items: [batch], next };\n }\n\n private async fetchPipelinesPage(\n options: SyncOptions,\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const repos = this.settings.repoSlugs;\n if (repos.length === 0) {\n return { items: [], next: null };\n }\n const { idx, url: rawPageUrl } = decodePage(page);\n if (idx >= repos.length) {\n return { items: [], next: null };\n }\n const slug = repos[idx]!;\n const expectedPath = this.pipelinesPath(slug);\n const fetchUrl =\n this.sanitizeUrl(rawPageUrl, expectedPath) ??\n this.buildPipelinesUrl(slug, options);\n const res = await this.fetch<BitbucketPipelinesResponse>(\n fetchUrl,\n 'pipelines',\n signal,\n );\n const rows = res.body.values;\n const cutoff = options.since ? parseEpoch(options.since, 'iso') : null;\n let filtered: BitbucketPipeline[];\n let cutoffReached: boolean;\n if (cutoff !== null) {\n filtered = rows.filter((p) => {\n const ts = parseEpoch(p.created_on, 'iso');\n return ts === null || ts >= cutoff;\n });\n const last = rows.at(-1);\n const lastTs = last ? parseEpoch(last.created_on, 'iso') : null;\n cutoffReached = lastTs !== null && lastTs < cutoff;\n } else {\n filtered = rows;\n cutoffReached = false;\n }\n const safeNext = this.sanitizeUrl(res.body.next ?? null, expectedPath);\n const nextWithinRepo = cutoffReached ? null : safeNext;\n const batch: RepoBatch<BitbucketPipeline> = {\n repoSlug: slug,\n items: filtered,\n };\n if (nextWithinRepo !== null) {\n return { items: [batch], next: encodePage(idx, nextWithinRepo) };\n }\n const nextIdx = idx + 1;\n const next = nextIdx < repos.length ? encodePage(nextIdx, null) : null;\n return { items: [batch], next };\n }\n\n // -------------------------------------------------------------------------\n // Writers\n // -------------------------------------------------------------------------\n\n private async writePullRequests(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n options: SyncOptions,\n ): Promise<void> {\n if (page === null && !options.since) {\n await storage.entities([], { types: ['pull_request'] });\n }\n const batches = items as RepoBatch<BitbucketPullRequest>[];\n for (const batch of batches) {\n for (const pr of batch.items) {\n const createdMs = parseEpoch(pr.created_on, 'iso');\n const updatedMs = parseEpoch(pr.updated_on, 'iso');\n if (createdMs === null || updatedMs === null) {\n continue;\n }\n await storage.entity({\n type: 'pull_request',\n id: `${this.settings.workspace}/${batch.repoSlug}:${pr.id}`,\n attributes: {\n workspace: this.settings.workspace,\n repo_slug: batch.repoSlug,\n pull_request_id: pr.id,\n title: pr.title,\n state: pr.state,\n author: pr.author?.nickname ?? pr.author?.display_name ?? null,\n source_branch: pr.source?.branch?.name ?? null,\n destination_branch: pr.destination?.branch?.name ?? null,\n web_url: pr.links?.html?.href ?? null,\n created_at: createdMs,\n closed_at: parseEpoch(pr.closed_on ?? null, 'iso'),\n },\n updated_at: updatedMs,\n });\n }\n }\n }\n\n private async writePipelines(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n options: SyncOptions,\n seenPipelineIds: Set<string>,\n ): Promise<void> {\n const pipelineAllowed = this.isResourceAllowed(\n 'pipeline',\n options.resources,\n );\n const eventAllowed = this.isResourceAllowed(\n 'pipeline_event',\n options.resources,\n );\n if (page === null && !options.since) {\n if (pipelineAllowed) {\n await storage.entities([], { types: ['pipeline'] });\n }\n if (eventAllowed) {\n await storage.events([], { names: ['pipeline_event'] });\n }\n }\n const batches = items as RepoBatch<BitbucketPipeline>[];\n for (const batch of batches) {\n for (const pipeline of batch.items) {\n const createdMs = parseEpoch(pipeline.created_on, 'iso');\n if (createdMs === null) {\n continue;\n }\n const entityId = `${this.settings.workspace}/${batch.repoSlug}:${pipeline.uuid}`;\n if (seenPipelineIds.has(entityId)) {\n continue;\n }\n seenPipelineIds.add(entityId);\n const completedMs = parseEpoch(pipeline.completed_on ?? null, 'iso');\n const durationMs =\n pipeline.duration_in_seconds !== null &&\n pipeline.duration_in_seconds !== undefined\n ? Math.round(pipeline.duration_in_seconds * 1000)\n : completedMs !== null\n ? completedMs - createdMs\n : null;\n const result = pipeline.state.result?.name ?? null;\n const refName = pipeline.target?.ref_name ?? null;\n const commitHash = pipeline.target?.commit?.hash ?? null;\n const triggerType = pipeline.trigger?.type ?? null;\n if (pipelineAllowed) {\n await storage.entity({\n type: 'pipeline',\n id: entityId,\n attributes: {\n workspace: this.settings.workspace,\n repo_slug: batch.repoSlug,\n uuid: pipeline.uuid,\n build_number: pipeline.build_number,\n state: pipeline.state.name,\n result,\n ref_name: refName,\n commit: commitHash,\n trigger_type: triggerType,\n creator:\n pipeline.creator?.nickname ??\n pipeline.creator?.display_name ??\n null,\n created_at: createdMs,\n completed_at: completedMs,\n duration_ms: durationMs,\n },\n updated_at: completedMs ?? createdMs,\n });\n }\n if (eventAllowed) {\n await storage.event({\n name: 'pipeline_event',\n start_ts: createdMs,\n end_ts: completedMs,\n attributes: {\n workspace: this.settings.workspace,\n repo_slug: batch.repoSlug,\n uuid: pipeline.uuid,\n build_number: pipeline.build_number,\n state: pipeline.state.name,\n result,\n ref_name: refName,\n commit: commitHash,\n trigger_type: triggerType,\n duration_ms: durationMs,\n },\n });\n }\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // Cursor resume\n // -------------------------------------------------------------------------\n\n private resolveCursor(cursor: unknown): BitbucketSyncCursor | undefined {\n if (!isBitbucketSyncCursor(cursor)) {\n return undefined;\n }\n return { phase: cursor.phase, page: cursor.page };\n }\n\n // -------------------------------------------------------------------------\n // sync()\n // -------------------------------------------------------------------------\n\n async sync(\n options: SyncOptions,\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<SyncResult> {\n const cursor = this.resolveCursor(options.cursor);\n const phases = this.activePhases(options.resources);\n const seenPipelineIds = new Set<string>();\n return paginateChunked<BitbucketPhase, string>({\n phases,\n cursor,\n signal,\n logger: this.logger,\n fetchPage: async (phase, page, sig) => {\n switch (phase) {\n case 'pull_requests':\n return this.fetchPullRequestsPage(options, page, sig);\n case 'pipelines':\n return this.fetchPipelinesPage(options, page, sig);\n }\n },\n writeBatch: async (phase, items, page) => {\n switch (phase) {\n case 'pull_requests':\n return this.writePullRequests(storage, items, page, options);\n case 'pipelines':\n return this.writePipelines(\n storage,\n items,\n page,\n options,\n seenPipelineIds,\n );\n }\n },\n });\n }\n}\n","import { BitbucketConnector } from './bitbucket';\n\nexport {\n bitbucketResources as resources,\n BitbucketConnector,\n configFields,\n doc,\n id,\n} from './bitbucket';\nexport type { BitbucketResource, BitbucketSettings } from './bitbucket';\nexport default BitbucketConnector;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rawdash/connector-bitbucket",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "Rawdash connector for Bitbucket Cloud — pull requests, pipelines, and pipeline state-transition events",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"zod": "^4.4.3",
|
|
27
|
-
"@rawdash/core": "0.
|
|
27
|
+
"@rawdash/core": "0.24.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"fast-check": "^4.8.0",
|