@rawdash/connector-bitbucket 0.25.0 → 0.26.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/README.md CHANGED
@@ -15,26 +15,26 @@ npm install @rawdash/connector-bitbucket
15
15
 
16
16
  ## Authentication
17
17
 
18
- 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.
18
+ Authenticates over HTTP Basic auth using an Atlassian account email as the username and an Atlassian API token as the password. The token grants access to the workspaces, repositories, and pipelines the account can already read.
19
19
 
20
- 1. Open Bitbucket -> Personal settings -> App passwords (https://bitbucket.org/account/settings/app-passwords/).
21
- 2. Create an app password with `Repositories:Read` and `Pipelines:Read` scopes.
22
- 3. 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.
20
+ 1. Open https://id.atlassian.com/manage-profile/security/api-tokens and create an API token for your Atlassian account. If you create a scoped token, include read access to repositories and pipelines.
21
+ 2. Store the token as a secret and reference it from the connector config as `apiToken: secret("BITBUCKET_API_TOKEN")`.
22
+ 3. Set `email` to the Atlassian account email that owns the token, alongside your `workspace` and the list of `repoSlugs` to sync.
23
23
 
24
24
  ## Configuration
25
25
 
26
- | Field | Type | Required | Description |
27
- | ------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
28
- | `workspace` | string | Yes | Bitbucket Cloud workspace slug (the segment shown in repo URLs after bitbucket.org/). |
29
- | `username` | string | Yes | Atlassian account username paired with the app password for Basic auth (find it under Personal settings -> Account settings). |
30
- | `appPassword` | secret | Yes | Bitbucket app password with `Repositories:Read` and `Pipelines:Read` scopes. Create one at Personal settings -> App passwords. |
31
- | `repoSlugs` | array | Yes | Repositories to sync, named by their slug within the workspace (no `workspace/` prefix). |
32
- | `resources` | array | No | 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. |
26
+ | Field | Type | Required | Description |
27
+ | ----------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
28
+ | `workspace` | string | Yes | Bitbucket Cloud workspace slug (the segment shown in repo URLs after bitbucket.org/). |
29
+ | `email` | string | Yes | Email address of the Atlassian account that owns the API token. Used as the Basic auth username. |
30
+ | `apiToken` | secret | Yes | Atlassian API token for the account, granting read access to the workspace repositories and pipelines you want to sync. Create one at https://id.atlassian.com/manage-profile/security/api-tokens. |
31
+ | `repoSlugs` | array | Yes | Repositories to sync, named by their slug within the workspace (no `workspace/` prefix). |
32
+ | `resources` | array | No | 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. |
33
33
 
34
34
  ## Resources
35
35
 
36
36
  - **`pull_request`** _(entity)_ - Open, merged, declined, and superseded pull requests with author, source/target branches, and close timestamp.
37
- - Endpoint: `GET /2.0/repositories/{workspace}/{repo_slug}/pullrequests?state=OPEN,MERGED,DECLINED,SUPERSEDED`
37
+ - Endpoint: `GET /2.0/repositories/{workspace}/{repo_slug}/pullrequests?state=OPEN&state=MERGED&state=DECLINED&state=SUPERSEDED`
38
38
  - Paginated newest-first by `updated_on`; the connector stops once a page is entirely older than `options.since`.
39
39
  - **`pipeline`** _(entity)_ - Bitbucket Pipelines runs with state, result, target ref/commit, trigger, duration, and create/complete timestamps.
40
40
  - Endpoint: `GET /2.0/repositories/{workspace}/{repo_slug}/pipelines/`
@@ -58,8 +58,8 @@ const bitbucket = {
58
58
  connectorId: 'bitbucket',
59
59
  config: {
60
60
  workspace: 'my-workspace',
61
- username: 'janedoe',
62
- appPassword: secret('BITBUCKET_APP_PASSWORD'),
61
+ email: 'jane@example.com',
62
+ apiToken: secret('BITBUCKET_API_TOKEN'),
63
63
  repoSlugs: ['my-repo'],
64
64
  },
65
65
  };
@@ -88,7 +88,7 @@ export default defineConfig({
88
88
 
89
89
  ## Rate limits
90
90
 
91
- 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).
91
+ Bitbucket Cloud applies hourly per-IP and per-user limits (around 1,000 requests/hour for API-token Basic auth). Pagination uses a `next` URL in each response and a configurable `pagelen` (capped at 50 here).
92
92
 
93
93
  ## Limitations
94
94
 
package/dist/index.d.ts CHANGED
@@ -3,8 +3,8 @@ import { z } from 'zod';
3
3
 
4
4
  declare const configFields: z.ZodObject<{
5
5
  workspace: z.ZodString;
6
- username: z.ZodString;
7
- appPassword: z.ZodObject<{
6
+ email: z.ZodString;
7
+ apiToken: z.ZodObject<{
8
8
  $secret: z.ZodString;
9
9
  }, z.core.$strip>;
10
10
  repoSlugs: z.ZodArray<z.ZodString>;
@@ -22,11 +22,11 @@ interface BitbucketSettings {
22
22
  resources?: readonly BitbucketResource[];
23
23
  }
24
24
  declare const bitbucketCredentials: {
25
- username: {
25
+ email: {
26
26
  description: string;
27
27
  auth: "required";
28
28
  };
29
- appPassword: {
29
+ apiToken: {
30
30
  description: string;
31
31
  auth: "required";
32
32
  };
@@ -36,7 +36,7 @@ declare const bitbucketResources: {
36
36
  readonly pull_request: {
37
37
  readonly shape: "entity";
38
38
  readonly description: "Open, merged, declined, and superseded pull requests with author, source/target branches, and close timestamp.";
39
- readonly endpoint: "GET /2.0/repositories/{workspace}/{repo_slug}/pullrequests?state=OPEN,MERGED,DECLINED,SUPERSEDED";
39
+ readonly endpoint: "GET /2.0/repositories/{workspace}/{repo_slug}/pullrequests?state=OPEN&state=MERGED&state=DECLINED&state=SUPERSEDED";
40
40
  readonly notes: "Paginated newest-first by `updated_on`; the connector stops once a page is entirely older than `options.since`.";
41
41
  readonly filterable: [{
42
42
  readonly field: "state";
@@ -150,7 +150,7 @@ declare class BitbucketConnector extends BaseConnector<BitbucketSettings, Bitbuc
150
150
  readonly pull_request: {
151
151
  readonly shape: "entity";
152
152
  readonly description: "Open, merged, declined, and superseded pull requests with author, source/target branches, and close timestamp.";
153
- readonly endpoint: "GET /2.0/repositories/{workspace}/{repo_slug}/pullrequests?state=OPEN,MERGED,DECLINED,SUPERSEDED";
153
+ readonly endpoint: "GET /2.0/repositories/{workspace}/{repo_slug}/pullrequests?state=OPEN&state=MERGED&state=DECLINED&state=SUPERSEDED";
154
154
  readonly notes: "Paginated newest-first by `updated_on`; the connector stops once a page is entirely older than `options.since`.";
155
155
  readonly filterable: [{
156
156
  readonly field: "state";
@@ -343,11 +343,11 @@ declare class BitbucketConnector extends BaseConnector<BitbucketSettings, Bitbuc
343
343
  static create(input: unknown, ctx?: ConnectorContext): BitbucketConnector;
344
344
  readonly id = "bitbucket";
345
345
  readonly credentials: {
346
- username: {
346
+ email: {
347
347
  description: string;
348
348
  auth: "required";
349
349
  };
350
- appPassword: {
350
+ apiToken: {
351
351
  description: string;
352
352
  auth: "required";
353
353
  };
package/dist/index.js CHANGED
@@ -52,15 +52,15 @@ var configFields = defineConfigFields(
52
52
  description: "Bitbucket Cloud workspace slug (the segment shown in repo URLs after bitbucket.org/).",
53
53
  placeholder: "my-workspace"
54
54
  }),
55
- username: z.string().min(1).meta({
56
- label: "Atlassian username",
57
- description: "Atlassian account username paired with the app password for Basic auth (find it under Personal settings -> Account settings).",
58
- placeholder: "janedoe"
55
+ email: z.string().min(1).meta({
56
+ label: "Atlassian account email",
57
+ description: "Email address of the Atlassian account that owns the API token. Used as the Basic auth username.",
58
+ placeholder: "jane@example.com"
59
59
  }),
60
- appPassword: z.object({ $secret: z.string() }).meta({
61
- label: "App password",
62
- description: "Bitbucket app password with `Repositories:Read` and `Pipelines:Read` scopes. Create one at Personal settings -> App passwords.",
63
- placeholder: "ATBB...",
60
+ apiToken: z.object({ $secret: z.string() }).meta({
61
+ label: "API token",
62
+ description: "Atlassian API token for the account, granting read access to the workspace repositories and pipelines you want to sync. Create one at https://id.atlassian.com/manage-profile/security/api-tokens.",
63
+ placeholder: "ATATT...",
64
64
  secret: true
65
65
  }),
66
66
  repoSlugs: z.array(repoSlug).nonempty().refine((slugs) => new Set(slugs).size === slugs.length, {
@@ -87,14 +87,14 @@ var doc = defineConnectorDoc({
87
87
  website: "https://bitbucket.org"
88
88
  },
89
89
  auth: {
90
- summary: "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.",
90
+ summary: "Authenticates over HTTP Basic auth using an Atlassian account email as the username and an Atlassian API token as the password. The token grants access to the workspaces, repositories, and pipelines the account can already read.",
91
91
  setup: [
92
- "Open Bitbucket -> Personal settings -> App passwords (https://bitbucket.org/account/settings/app-passwords/).",
93
- "Create an app password with `Repositories:Read` and `Pipelines:Read` scopes.",
94
- '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.'
92
+ "Open https://id.atlassian.com/manage-profile/security/api-tokens and create an API token for your Atlassian account. If you create a scoped token, include read access to repositories and pipelines.",
93
+ 'Store the token as a secret and reference it from the connector config as `apiToken: secret("BITBUCKET_API_TOKEN")`.',
94
+ "Set `email` to the Atlassian account email that owns the token, alongside your `workspace` and the list of `repoSlugs` to sync."
95
95
  ]
96
96
  },
97
- rateLimit: "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).",
97
+ rateLimit: "Bitbucket Cloud applies hourly per-IP and per-user limits (around 1,000 requests/hour for API-token Basic auth). Pagination uses a `next` URL in each response and a configurable `pagelen` (capped at 50 here).",
98
98
  limitations: [
99
99
  "Bitbucket Server / Data Center are out of scope; this connector targets Bitbucket Cloud only.",
100
100
  "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.",
@@ -102,12 +102,12 @@ var doc = defineConnectorDoc({
102
102
  ]
103
103
  });
104
104
  var bitbucketCredentials = {
105
- username: {
106
- description: "Atlassian account username",
105
+ email: {
106
+ description: "Atlassian account email",
107
107
  auth: "required"
108
108
  },
109
- appPassword: {
110
- description: "Bitbucket app password",
109
+ apiToken: {
110
+ description: "Atlassian API token",
111
111
  auth: "required"
112
112
  }
113
113
  };
@@ -202,7 +202,7 @@ var bitbucketResources = defineResources({
202
202
  pull_request: {
203
203
  shape: "entity",
204
204
  description: "Open, merged, declined, and superseded pull requests with author, source/target branches, and close timestamp.",
205
- endpoint: "GET /2.0/repositories/{workspace}/{repo_slug}/pullrequests?state=OPEN,MERGED,DECLINED,SUPERSEDED",
205
+ endpoint: "GET /2.0/repositories/{workspace}/{repo_slug}/pullrequests?state=OPEN&state=MERGED&state=DECLINED&state=SUPERSEDED",
206
206
  notes: "Paginated newest-first by `updated_on`; the connector stops once a page is entirely older than `options.since`.",
207
207
  filterable: [
208
208
  {
@@ -230,12 +230,13 @@ var bitbucketResources = defineResources({
230
230
  }
231
231
  });
232
232
  var id = "bitbucket";
233
- var PULL_REQUEST_STATES = /* @__PURE__ */ new Set([
233
+ var PULL_REQUEST_STATE_VALUES = [
234
234
  "OPEN",
235
235
  "MERGED",
236
236
  "DECLINED",
237
237
  "SUPERSEDED"
238
- ]);
238
+ ];
239
+ var PULL_REQUEST_STATES = new Set(PULL_REQUEST_STATE_VALUES);
239
240
  function pushablePullRequestState(filter) {
240
241
  if (!filter) {
241
242
  return null;
@@ -259,14 +260,14 @@ var BitbucketConnector = class _BitbucketConnector extends BaseConnector {
259
260
  repoSlugs: parsed.repoSlugs,
260
261
  resources: parsed.resources
261
262
  },
262
- { username: parsed.username, appPassword: parsed.appPassword },
263
+ { email: parsed.email, apiToken: parsed.apiToken },
263
264
  ctx
264
265
  );
265
266
  }
266
267
  id = id;
267
268
  credentials = bitbucketCredentials;
268
269
  buildHeaders() {
269
- const basic = btoa(`${this.creds.username}:${this.creds.appPassword}`);
270
+ const basic = btoa(`${this.creds.email}:${this.creds.apiToken}`);
270
271
  return {
271
272
  Authorization: `Basic ${basic}`,
272
273
  Accept: "application/json",
@@ -343,10 +344,11 @@ var BitbucketConnector = class _BitbucketConnector extends BaseConnector {
343
344
  );
344
345
  u.searchParams.set("pagelen", String(PAGE_SIZE));
345
346
  u.searchParams.set("sort", "-updated_on");
346
- u.searchParams.set(
347
- "state",
348
- pushablePullRequestState(spec?.filter) ?? "OPEN,MERGED,DECLINED,SUPERSEDED"
349
- );
347
+ const pushedState = pushablePullRequestState(spec?.filter);
348
+ const states = pushedState ? [pushedState] : PULL_REQUEST_STATE_VALUES;
349
+ for (const state of states) {
350
+ u.searchParams.append("state", state);
351
+ }
350
352
  if (options.since) {
351
353
  u.searchParams.set("q", `updated_on >= ${options.since}`);
352
354
  }
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 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":[]}
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 email: z.string().min(1).meta({\n label: 'Atlassian account email',\n description:\n 'Email address of the Atlassian account that owns the API token. Used as the Basic auth username.',\n placeholder: 'jane@example.com',\n }),\n apiToken: z.object({ $secret: z.string() }).meta({\n label: 'API token',\n description:\n 'Atlassian API token for the account, granting read access to the workspace repositories and pipelines you want to sync. Create one at https://id.atlassian.com/manage-profile/security/api-tokens.',\n placeholder: 'ATATT...',\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 email as the username and an Atlassian API token as the password. The token grants access to the workspaces, repositories, and pipelines the account can already read.',\n setup: [\n 'Open https://id.atlassian.com/manage-profile/security/api-tokens and create an API token for your Atlassian account. If you create a scoped token, include read access to repositories and pipelines.',\n 'Store the token as a secret and reference it from the connector config as `apiToken: secret(\"BITBUCKET_API_TOKEN\")`.',\n 'Set `email` to the Atlassian account email that owns the token, alongside your `workspace` 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 API-token Basic 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 email: {\n description: 'Atlassian account email',\n auth: 'required' as const,\n },\n apiToken: {\n description: 'Atlassian API token',\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&state=MERGED&state=DECLINED&state=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_STATE_VALUES = [\n 'OPEN',\n 'MERGED',\n 'DECLINED',\n 'SUPERSEDED',\n] as const;\n\nconst PULL_REQUEST_STATES = new Set<string>(PULL_REQUEST_STATE_VALUES);\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 { email: parsed.email, apiToken: parsed.apiToken },\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.email}:${this.creds.apiToken}`);\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 const pushedState = pushablePullRequestState(spec?.filter);\n const states = pushedState ? [pushedState] : PULL_REQUEST_STATE_VALUES;\n for (const state of states) {\n u.searchParams.append('state', state);\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.25.0",
3
+ "version": "0.26.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,15 +24,15 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "zod": "^4.4.3",
27
- "@rawdash/core": "0.25.0"
27
+ "@rawdash/core": "0.26.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "fast-check": "^4.8.0",
31
31
  "tsup": "^8.0.0",
32
32
  "typescript": "^5.7.2",
33
33
  "vitest": "^4.1.4",
34
- "@rawdash/connector-shared": "0.3.1",
35
- "@rawdash/connector-test-utils": "0.0.10"
34
+ "@rawdash/connector-test-utils": "0.0.10",
35
+ "@rawdash/connector-shared": "0.3.1"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "tsup",