@rawdash/connector-gitlab 0.21.1 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +35 -0
- package/dist/index.js +50 -6
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ declare const gitlabResources: {
|
|
|
38
38
|
readonly description: "GitLab projects (repositories) with namespace path, default branch, and archived/visibility flags.";
|
|
39
39
|
readonly endpoint: "GET /api/v4/projects/{id}";
|
|
40
40
|
readonly notes: "Discovered from configured `projectIds` and from `groupIds` via GET /api/v4/groups/{id}/projects?include_subgroups=true.";
|
|
41
|
+
readonly filterable: [];
|
|
41
42
|
readonly responses: {
|
|
42
43
|
readonly projects: z.ZodArray<z.ZodObject<{
|
|
43
44
|
id: z.ZodNumber;
|
|
@@ -56,6 +57,11 @@ declare const gitlabResources: {
|
|
|
56
57
|
readonly shape: "entity";
|
|
57
58
|
readonly description: "Open, merged, and closed merge requests with author, source/target branches, and merge timestamps.";
|
|
58
59
|
readonly endpoint: "GET /api/v4/projects/{id}/merge_requests";
|
|
60
|
+
readonly filterable: [{
|
|
61
|
+
readonly field: "state";
|
|
62
|
+
readonly ops: ["eq"];
|
|
63
|
+
readonly values: ["opened", "closed", "merged", "locked"];
|
|
64
|
+
}];
|
|
59
65
|
readonly responses: {
|
|
60
66
|
readonly merge_requests: z.ZodArray<z.ZodObject<{
|
|
61
67
|
id: z.ZodNumber;
|
|
@@ -89,6 +95,10 @@ declare const gitlabResources: {
|
|
|
89
95
|
readonly shape: "entity";
|
|
90
96
|
readonly description: "CI/CD pipelines with status, ref, commit sha, source, duration, and start/finish timestamps.";
|
|
91
97
|
readonly endpoint: "GET /api/v4/projects/{id}/pipelines";
|
|
98
|
+
readonly filterable: [{
|
|
99
|
+
readonly field: "status";
|
|
100
|
+
readonly ops: ["eq"];
|
|
101
|
+
}];
|
|
92
102
|
readonly responses: {
|
|
93
103
|
readonly pipelines: z.ZodArray<z.ZodObject<{
|
|
94
104
|
id: z.ZodNumber;
|
|
@@ -112,11 +122,17 @@ declare const gitlabResources: {
|
|
|
112
122
|
readonly description: "Pipeline lifecycle events. One event per pipeline covering created_at to finished_at (or updated_at if not yet finished), tagged with the terminal status.";
|
|
113
123
|
readonly endpoint: "GET /api/v4/projects/{id}/pipelines";
|
|
114
124
|
readonly notes: "Derived from the same pipelines response that builds the `pipeline` resource; the GitLab API does not expose an intermediate state-transition history endpoint.";
|
|
125
|
+
readonly filterable: [];
|
|
115
126
|
};
|
|
116
127
|
readonly issue: {
|
|
117
128
|
readonly shape: "entity";
|
|
118
129
|
readonly description: "Open and closed issues with labels, author, assignees, and close timestamp.";
|
|
119
130
|
readonly endpoint: "GET /api/v4/projects/{id}/issues";
|
|
131
|
+
readonly filterable: [{
|
|
132
|
+
readonly field: "state";
|
|
133
|
+
readonly ops: ["eq"];
|
|
134
|
+
readonly values: ["opened", "closed"];
|
|
135
|
+
}];
|
|
120
136
|
readonly responses: {
|
|
121
137
|
readonly issues: z.ZodArray<z.ZodObject<{
|
|
122
138
|
id: z.ZodNumber;
|
|
@@ -146,6 +162,7 @@ declare const gitlabResources: {
|
|
|
146
162
|
readonly shape: "entity";
|
|
147
163
|
readonly description: "Project releases keyed by tag name, including released_at and the publishing author.";
|
|
148
164
|
readonly endpoint: "GET /api/v4/projects/{id}/releases";
|
|
165
|
+
readonly filterable: [];
|
|
149
166
|
readonly responses: {
|
|
150
167
|
readonly releases: z.ZodArray<z.ZodObject<{
|
|
151
168
|
tag_name: z.ZodString;
|
|
@@ -171,6 +188,7 @@ declare class GitLabConnector extends BaseConnector<GitLabSettings, GitLabCreden
|
|
|
171
188
|
readonly description: "GitLab projects (repositories) with namespace path, default branch, and archived/visibility flags.";
|
|
172
189
|
readonly endpoint: "GET /api/v4/projects/{id}";
|
|
173
190
|
readonly notes: "Discovered from configured `projectIds` and from `groupIds` via GET /api/v4/groups/{id}/projects?include_subgroups=true.";
|
|
191
|
+
readonly filterable: [];
|
|
174
192
|
readonly responses: {
|
|
175
193
|
readonly projects: z.ZodArray<z.ZodObject<{
|
|
176
194
|
id: z.ZodNumber;
|
|
@@ -189,6 +207,11 @@ declare class GitLabConnector extends BaseConnector<GitLabSettings, GitLabCreden
|
|
|
189
207
|
readonly shape: "entity";
|
|
190
208
|
readonly description: "Open, merged, and closed merge requests with author, source/target branches, and merge timestamps.";
|
|
191
209
|
readonly endpoint: "GET /api/v4/projects/{id}/merge_requests";
|
|
210
|
+
readonly filterable: [{
|
|
211
|
+
readonly field: "state";
|
|
212
|
+
readonly ops: ["eq"];
|
|
213
|
+
readonly values: ["opened", "closed", "merged", "locked"];
|
|
214
|
+
}];
|
|
192
215
|
readonly responses: {
|
|
193
216
|
readonly merge_requests: z.ZodArray<z.ZodObject<{
|
|
194
217
|
id: z.ZodNumber;
|
|
@@ -222,6 +245,10 @@ declare class GitLabConnector extends BaseConnector<GitLabSettings, GitLabCreden
|
|
|
222
245
|
readonly shape: "entity";
|
|
223
246
|
readonly description: "CI/CD pipelines with status, ref, commit sha, source, duration, and start/finish timestamps.";
|
|
224
247
|
readonly endpoint: "GET /api/v4/projects/{id}/pipelines";
|
|
248
|
+
readonly filterable: [{
|
|
249
|
+
readonly field: "status";
|
|
250
|
+
readonly ops: ["eq"];
|
|
251
|
+
}];
|
|
225
252
|
readonly responses: {
|
|
226
253
|
readonly pipelines: z.ZodArray<z.ZodObject<{
|
|
227
254
|
id: z.ZodNumber;
|
|
@@ -245,11 +272,17 @@ declare class GitLabConnector extends BaseConnector<GitLabSettings, GitLabCreden
|
|
|
245
272
|
readonly description: "Pipeline lifecycle events. One event per pipeline covering created_at to finished_at (or updated_at if not yet finished), tagged with the terminal status.";
|
|
246
273
|
readonly endpoint: "GET /api/v4/projects/{id}/pipelines";
|
|
247
274
|
readonly notes: "Derived from the same pipelines response that builds the `pipeline` resource; the GitLab API does not expose an intermediate state-transition history endpoint.";
|
|
275
|
+
readonly filterable: [];
|
|
248
276
|
};
|
|
249
277
|
readonly issue: {
|
|
250
278
|
readonly shape: "entity";
|
|
251
279
|
readonly description: "Open and closed issues with labels, author, assignees, and close timestamp.";
|
|
252
280
|
readonly endpoint: "GET /api/v4/projects/{id}/issues";
|
|
281
|
+
readonly filterable: [{
|
|
282
|
+
readonly field: "state";
|
|
283
|
+
readonly ops: ["eq"];
|
|
284
|
+
readonly values: ["opened", "closed"];
|
|
285
|
+
}];
|
|
253
286
|
readonly responses: {
|
|
254
287
|
readonly issues: z.ZodArray<z.ZodObject<{
|
|
255
288
|
id: z.ZodNumber;
|
|
@@ -279,6 +312,7 @@ declare class GitLabConnector extends BaseConnector<GitLabSettings, GitLabCreden
|
|
|
279
312
|
readonly shape: "entity";
|
|
280
313
|
readonly description: "Project releases keyed by tag name, including released_at and the publishing author.";
|
|
281
314
|
readonly endpoint: "GET /api/v4/projects/{id}/releases";
|
|
315
|
+
readonly filterable: [];
|
|
282
316
|
readonly responses: {
|
|
283
317
|
readonly releases: z.ZodArray<z.ZodObject<{
|
|
284
318
|
tag_name: z.ZodString;
|
|
@@ -413,6 +447,7 @@ declare class GitLabConnector extends BaseConnector<GitLabSettings, GitLabCreden
|
|
|
413
447
|
private fetchGroupProjects;
|
|
414
448
|
private fetchProjectMetadata;
|
|
415
449
|
private fetchProjectsPhase;
|
|
450
|
+
private singleSpec;
|
|
416
451
|
private buildListPageUrl;
|
|
417
452
|
private fetchListPhase;
|
|
418
453
|
private writeProjects;
|
package/dist/index.js
CHANGED
|
@@ -121,6 +121,7 @@ var doc = defineConnectorDoc({
|
|
|
121
121
|
tagline: "Sync projects, merge requests, pipelines, issues, and releases from GitLab.com or a self-hosted GitLab instance.",
|
|
122
122
|
vendor: {
|
|
123
123
|
name: "GitLab",
|
|
124
|
+
domain: "gitlab.com",
|
|
124
125
|
apiDocs: "https://docs.gitlab.com/ee/api/",
|
|
125
126
|
website: "https://gitlab.com"
|
|
126
127
|
},
|
|
@@ -262,40 +263,69 @@ var gitlabResources = defineResources({
|
|
|
262
263
|
description: "GitLab projects (repositories) with namespace path, default branch, and archived/visibility flags.",
|
|
263
264
|
endpoint: "GET /api/v4/projects/{id}",
|
|
264
265
|
notes: "Discovered from configured `projectIds` and from `groupIds` via GET /api/v4/groups/{id}/projects?include_subgroups=true.",
|
|
266
|
+
filterable: [],
|
|
265
267
|
responses: { projects: projectsResponseSchema }
|
|
266
268
|
},
|
|
267
269
|
merge_request: {
|
|
268
270
|
shape: "entity",
|
|
269
271
|
description: "Open, merged, and closed merge requests with author, source/target branches, and merge timestamps.",
|
|
270
272
|
endpoint: "GET /api/v4/projects/{id}/merge_requests",
|
|
273
|
+
filterable: [
|
|
274
|
+
{
|
|
275
|
+
field: "state",
|
|
276
|
+
ops: ["eq"],
|
|
277
|
+
values: ["opened", "closed", "merged", "locked"]
|
|
278
|
+
}
|
|
279
|
+
],
|
|
271
280
|
responses: { merge_requests: mergeRequestsResponseSchema }
|
|
272
281
|
},
|
|
273
282
|
pipeline: {
|
|
274
283
|
shape: "entity",
|
|
275
284
|
description: "CI/CD pipelines with status, ref, commit sha, source, duration, and start/finish timestamps.",
|
|
276
285
|
endpoint: "GET /api/v4/projects/{id}/pipelines",
|
|
286
|
+
filterable: [{ field: "status", ops: ["eq"] }],
|
|
277
287
|
responses: { pipelines: pipelinesResponseSchema }
|
|
278
288
|
},
|
|
279
289
|
pipeline_event: {
|
|
280
290
|
shape: "event",
|
|
281
291
|
description: "Pipeline lifecycle events. One event per pipeline covering created_at to finished_at (or updated_at if not yet finished), tagged with the terminal status.",
|
|
282
292
|
endpoint: "GET /api/v4/projects/{id}/pipelines",
|
|
283
|
-
notes: "Derived from the same pipelines response that builds the `pipeline` resource; the GitLab API does not expose an intermediate state-transition history endpoint."
|
|
293
|
+
notes: "Derived from the same pipelines response that builds the `pipeline` resource; the GitLab API does not expose an intermediate state-transition history endpoint.",
|
|
294
|
+
filterable: []
|
|
284
295
|
},
|
|
285
296
|
issue: {
|
|
286
297
|
shape: "entity",
|
|
287
298
|
description: "Open and closed issues with labels, author, assignees, and close timestamp.",
|
|
288
299
|
endpoint: "GET /api/v4/projects/{id}/issues",
|
|
300
|
+
filterable: [{ field: "state", ops: ["eq"], values: ["opened", "closed"] }],
|
|
289
301
|
responses: { issues: issuesResponseSchema }
|
|
290
302
|
},
|
|
291
303
|
release: {
|
|
292
304
|
shape: "entity",
|
|
293
305
|
description: "Project releases keyed by tag name, including released_at and the publishing author.",
|
|
294
306
|
endpoint: "GET /api/v4/projects/{id}/releases",
|
|
307
|
+
filterable: [],
|
|
295
308
|
responses: { releases: releasesResponseSchema }
|
|
296
309
|
}
|
|
297
310
|
});
|
|
298
311
|
var id = "gitlab";
|
|
312
|
+
var LIST_RESOURCE_DEF_KEY = {
|
|
313
|
+
merge_requests: "merge_request",
|
|
314
|
+
pipelines: "pipeline",
|
|
315
|
+
issues: "issue",
|
|
316
|
+
releases: "release"
|
|
317
|
+
};
|
|
318
|
+
function pushableEq(filter, field) {
|
|
319
|
+
if (!filter) {
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
for (const clause of filter) {
|
|
323
|
+
if ("field" in clause && clause.field === field && clause.op === "eq" && typeof clause.value === "string") {
|
|
324
|
+
return clause.value;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
299
329
|
var GitLabConnector = class _GitLabConnector extends BaseConnector {
|
|
300
330
|
static id = id;
|
|
301
331
|
static resources = gitlabResources;
|
|
@@ -470,12 +500,16 @@ var GitLabConnector = class _GitLabConnector extends BaseConnector {
|
|
|
470
500
|
const next = nextIdx < projects.length ? encodePage(nextIdx, null) : null;
|
|
471
501
|
return { items: project ? [project] : [], next };
|
|
472
502
|
}
|
|
473
|
-
|
|
503
|
+
singleSpec(options, resource) {
|
|
504
|
+
const specs = options.fetchSpecs?.[resource];
|
|
505
|
+
return specs && specs.length === 1 ? specs[0] : void 0;
|
|
506
|
+
}
|
|
507
|
+
buildListPageUrl(projectId, resource, options, spec) {
|
|
474
508
|
const u = new URL(`${this.apiBase()}/projects/${projectId}/${resource}`);
|
|
475
509
|
u.searchParams.set("per_page", String(PAGE_SIZE));
|
|
476
510
|
switch (resource) {
|
|
477
511
|
case "merge_requests":
|
|
478
|
-
u.searchParams.set("state", "all");
|
|
512
|
+
u.searchParams.set("state", pushableEq(spec?.filter, "state") ?? "all");
|
|
479
513
|
u.searchParams.set("order_by", "updated_at");
|
|
480
514
|
u.searchParams.set("sort", "desc");
|
|
481
515
|
u.searchParams.set("scope", "all");
|
|
@@ -483,15 +517,20 @@ var GitLabConnector = class _GitLabConnector extends BaseConnector {
|
|
|
483
517
|
u.searchParams.set("updated_after", options.since);
|
|
484
518
|
}
|
|
485
519
|
break;
|
|
486
|
-
case "pipelines":
|
|
520
|
+
case "pipelines": {
|
|
521
|
+
const status = pushableEq(spec?.filter, "status");
|
|
522
|
+
if (status !== null) {
|
|
523
|
+
u.searchParams.set("status", status);
|
|
524
|
+
}
|
|
487
525
|
u.searchParams.set("order_by", "updated_at");
|
|
488
526
|
u.searchParams.set("sort", "desc");
|
|
489
527
|
if (options.since) {
|
|
490
528
|
u.searchParams.set("updated_after", options.since);
|
|
491
529
|
}
|
|
492
530
|
break;
|
|
531
|
+
}
|
|
493
532
|
case "issues":
|
|
494
|
-
u.searchParams.set("state", "all");
|
|
533
|
+
u.searchParams.set("state", pushableEq(spec?.filter, "state") ?? "all");
|
|
495
534
|
u.searchParams.set("order_by", "updated_at");
|
|
496
535
|
u.searchParams.set("sort", "desc");
|
|
497
536
|
u.searchParams.set("scope", "all");
|
|
@@ -517,7 +556,12 @@ var GitLabConnector = class _GitLabConnector extends BaseConnector {
|
|
|
517
556
|
}
|
|
518
557
|
const projectId = projects[idx];
|
|
519
558
|
const expectedPath = `/api/v4/projects/${projectId}/${resource}`;
|
|
520
|
-
const fetchUrl = this.sanitizeUrl(rawPageUrl, expectedPath) ?? this.buildListPageUrl(
|
|
559
|
+
const fetchUrl = this.sanitizeUrl(rawPageUrl, expectedPath) ?? this.buildListPageUrl(
|
|
560
|
+
projectId,
|
|
561
|
+
resource,
|
|
562
|
+
options,
|
|
563
|
+
this.singleSpec(options, LIST_RESOURCE_DEF_KEY[resource])
|
|
564
|
+
);
|
|
521
565
|
const res = await this.fetch(fetchUrl, resource, signal);
|
|
522
566
|
const rawNext = parseLinkHeader(res.headers.get("link"))["next"] ?? null;
|
|
523
567
|
const safeNext = this.sanitizeUrl(rawNext, expectedPath);
|
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/gitlab.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 parseLinkHeader,\n standardRateLimitPolicy,\n} from '@rawdash/connector-shared';\nimport {\n BaseConnector,\n type ChunkedSyncCursor,\n type ConnectorContext,\n type ConnectorDoc,\n type CredentialsSchema,\n type Event,\n type FetchPageResult,\n type StorageHandle,\n type SyncOptions,\n type SyncResult,\n defineConfigFields,\n defineConnectorDoc,\n defineResources,\n makeChunkedCursorGuard,\n paginateChunked,\n schemasFromResources,\n selectActivePhases,\n} from '@rawdash/core';\nimport { z } from 'zod';\n\nconst positiveInt = z.number().int().positive();\n\nexport const configFields = defineConfigFields(\n z\n .object({\n apiToken: z.object({ $secret: z.string() }).meta({\n label: 'API Token',\n description:\n 'GitLab Personal Access Token with `read_api` scope. Create one at GitLab -> Preferences -> Access Tokens.',\n placeholder: 'glpat-...',\n secret: true,\n }),\n host: z\n .string()\n .min(1)\n .regex(\n /^[^/\\s:?#]+$/,\n 'Use host only (no protocol, port, path, or query).',\n )\n .optional()\n .meta({\n label: 'Host (optional)',\n description:\n 'Your GitLab host. Defaults to `gitlab.com`. For self-hosted, supply the hostname only (e.g. `gitlab.example.com`).',\n placeholder: 'gitlab.com',\n }),\n projectIds: z.array(positiveInt).nonempty().optional().meta({\n label: 'Project IDs (optional)',\n description:\n 'Numeric project IDs to sync directly (find one in Project -> Settings -> General). Combined with any projects discovered via `groupIds`.',\n }),\n groupIds: z.array(positiveInt).nonempty().optional().meta({\n label: 'Group IDs (optional)',\n description:\n 'Numeric group IDs whose projects (including subgroups) will be discovered and synced.',\n }),\n resources: z\n .array(\n z.enum([\n 'project',\n 'merge_request',\n 'pipeline',\n 'pipeline_event',\n 'issue',\n 'release',\n ]),\n )\n .nonempty()\n .optional()\n .meta({\n label: 'Resources',\n description:\n \"Which GitLab 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 .refine(\n (v) =>\n (v.projectIds && v.projectIds.length > 0) ||\n (v.groupIds && v.groupIds.length > 0),\n {\n message: 'At least one of `projectIds` or `groupIds` must be provided.',\n path: ['projectIds'],\n },\n ),\n);\n\nexport const doc: ConnectorDoc = defineConnectorDoc({\n displayName: 'GitLab',\n category: 'engineering',\n brandColor: '#FC6D26',\n tagline:\n 'Sync projects, merge requests, pipelines, issues, and releases from GitLab.com or a self-hosted GitLab instance.',\n vendor: {\n name: 'GitLab',\n apiDocs: 'https://docs.gitlab.com/ee/api/',\n website: 'https://gitlab.com',\n },\n auth: {\n summary:\n 'A GitLab Personal Access Token (PAT) with the `read_api` scope is required. The PAT must belong to an account with read access to the projects and groups you want to sync. Self-hosted GitLab is supported by overriding the `host` field.',\n setup: [\n 'Open GitLab -> User Preferences -> Access Tokens (or the equivalent on your self-hosted instance).',\n 'Create a Personal Access Token with the `read_api` scope.',\n 'Store it as a secret and reference it from the connector config as `apiToken: secret(\"GITLAB_API_TOKEN\")`.',\n 'Set `projectIds` to a list of numeric project IDs, or `groupIds` to a list of numeric group IDs (or both). At least one must be set.',\n 'For self-hosted GitLab, set `host` to your instance hostname (no protocol or path), e.g. `gitlab.example.com`.',\n ],\n },\n rateLimit:\n 'GitLab returns standard `RateLimit-Remaining` / `RateLimit-Reset` headers (reset is a Unix timestamp in seconds); list pagination uses the Link header (page size 100).',\n limitations: [\n 'Container Registry, Packages, and GitLab Duo / AI features are out of scope.',\n 'Pipeline state-transition events are synthesized: one `pipeline_event` is emitted per pipeline lifecycle (created_at to finished_at/updated_at), not one per intermediate state change.',\n 'Group project discovery walks each group with `include_subgroups=true`; very large groups may take multiple sync chunks to enumerate.',\n ],\n});\n\nexport type GitLabResource =\n | 'project'\n | 'merge_request'\n | 'pipeline'\n | 'pipeline_event'\n | 'issue'\n | 'release';\n\nexport interface GitLabSettings {\n host: string;\n projectIds?: readonly number[];\n groupIds?: readonly number[];\n resources?: readonly GitLabResource[];\n}\n\nconst gitlabCredentials = {\n apiToken: {\n description: 'GitLab Personal Access Token',\n auth: 'required' as const,\n },\n} satisfies CredentialsSchema;\n\ntype GitLabCredentials = typeof gitlabCredentials;\n\nconst DEFAULT_HOST = 'gitlab.com';\nconst PAGE_SIZE = 100;\n\nconst gitlabRateLimit = standardRateLimitPolicy({\n remainingHeader: 'ratelimit-remaining',\n resetHeader: 'ratelimit-reset',\n resetUnit: 's',\n});\n\nconst PHASE_ORDER = [\n 'projects',\n 'merge_requests',\n 'pipelines',\n 'issues',\n 'releases',\n] as const;\n\ntype GitLabPhase = (typeof PHASE_ORDER)[number];\n\ntype GitLabSyncCursor = ChunkedSyncCursor<GitLabPhase, string>;\n\nconst isGitLabSyncCursor = makeChunkedCursorGuard(PHASE_ORDER);\n\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\ninterface GitLabUserRef {\n id: number;\n username: string;\n name?: string | null;\n}\n\ninterface GitLabProject {\n id: number;\n name: string;\n path_with_namespace: string;\n default_branch: string | null;\n web_url: string;\n created_at: string;\n last_activity_at?: string | null;\n archived?: boolean;\n visibility?: string;\n}\n\ninterface GitLabMergeRequest {\n id: number;\n iid: number;\n project_id: number;\n title: string;\n state: string;\n draft?: boolean;\n work_in_progress?: boolean;\n author: GitLabUserRef | null;\n assignees?: GitLabUserRef[];\n source_branch: string;\n target_branch: string;\n created_at: string;\n updated_at: string;\n merged_at: string | null;\n closed_at: string | null;\n web_url: string;\n}\n\ninterface GitLabPipeline {\n id: number;\n iid?: number;\n project_id: number;\n status: string;\n ref: string | null;\n sha: string;\n source: string | null;\n created_at: string;\n updated_at: string;\n started_at?: string | null;\n finished_at?: string | null;\n duration?: number | null;\n web_url: string;\n}\n\ninterface GitLabIssue {\n id: number;\n iid: number;\n project_id: number;\n title: string;\n state: string;\n labels: string[];\n author: GitLabUserRef | null;\n assignees?: GitLabUserRef[];\n created_at: string;\n updated_at: string;\n closed_at: string | null;\n web_url: string;\n}\n\ninterface GitLabRelease {\n tag_name: string;\n name: string | null;\n description?: string | null;\n created_at: string;\n released_at: string | null;\n author?: GitLabUserRef | null;\n}\n\nconst userRefSchema = z.object({\n id: z.number().int(),\n username: z.string().min(1),\n name: z.string().nullable().optional(),\n});\n\nconst projectSchema = z.object({\n id: z.number().int(),\n name: z.string().min(1),\n path_with_namespace: z.string().min(1),\n default_branch: z.string().nullable(),\n web_url: z.string(),\n created_at: z.iso.datetime(),\n last_activity_at: z.iso.datetime().nullable().optional(),\n archived: z.boolean().optional(),\n visibility: z.string().optional(),\n});\n\nconst projectsResponseSchema = z.array(projectSchema);\n\nconst mergeRequestSchema = z.object({\n id: z.number().int(),\n iid: z.number().int(),\n project_id: z.number().int(),\n title: z.string(),\n state: z.string().min(1),\n draft: z.boolean().optional(),\n work_in_progress: z.boolean().optional(),\n author: userRefSchema.nullable(),\n assignees: z.array(userRefSchema).optional(),\n source_branch: z.string(),\n target_branch: z.string(),\n created_at: z.iso.datetime(),\n updated_at: z.iso.datetime(),\n merged_at: z.iso.datetime().nullable(),\n closed_at: z.iso.datetime().nullable(),\n web_url: z.string(),\n});\n\nconst mergeRequestsResponseSchema = z.array(mergeRequestSchema);\n\nconst pipelineSchema = z.object({\n id: z.number().int(),\n iid: z.number().int().optional(),\n project_id: z.number().int(),\n status: z.string().min(1),\n ref: z.string().nullable(),\n sha: z.string().min(1),\n source: z.string().nullable(),\n created_at: z.iso.datetime(),\n updated_at: z.iso.datetime(),\n started_at: z.iso.datetime().nullable().optional(),\n finished_at: z.iso.datetime().nullable().optional(),\n duration: z.number().nullable().optional(),\n web_url: z.string(),\n});\n\nconst pipelinesResponseSchema = z.array(pipelineSchema);\n\nconst issueSchema = z.object({\n id: z.number().int(),\n iid: z.number().int(),\n project_id: z.number().int(),\n title: z.string(),\n state: z.string().min(1),\n labels: z.array(z.string()),\n author: userRefSchema.nullable(),\n assignees: z.array(userRefSchema).optional(),\n created_at: z.iso.datetime(),\n updated_at: z.iso.datetime(),\n closed_at: z.iso.datetime().nullable(),\n web_url: z.string(),\n});\n\nconst issuesResponseSchema = z.array(issueSchema);\n\nconst releaseSchema = z.object({\n tag_name: z.string().min(1),\n name: z.string().nullable(),\n description: z.string().nullable().optional(),\n created_at: z.iso.datetime(),\n released_at: z.iso.datetime().nullable(),\n author: userRefSchema.nullable().optional(),\n});\n\nconst releasesResponseSchema = z.array(releaseSchema);\n\nexport const gitlabResources = defineResources({\n project: {\n shape: 'entity',\n description:\n 'GitLab projects (repositories) with namespace path, default branch, and archived/visibility flags.',\n endpoint: 'GET /api/v4/projects/{id}',\n notes:\n 'Discovered from configured `projectIds` and from `groupIds` via GET /api/v4/groups/{id}/projects?include_subgroups=true.',\n responses: { projects: projectsResponseSchema },\n },\n merge_request: {\n shape: 'entity',\n description:\n 'Open, merged, and closed merge requests with author, source/target branches, and merge timestamps.',\n endpoint: 'GET /api/v4/projects/{id}/merge_requests',\n responses: { merge_requests: mergeRequestsResponseSchema },\n },\n pipeline: {\n shape: 'entity',\n description:\n 'CI/CD pipelines with status, ref, commit sha, source, duration, and start/finish timestamps.',\n endpoint: 'GET /api/v4/projects/{id}/pipelines',\n responses: { pipelines: pipelinesResponseSchema },\n },\n pipeline_event: {\n shape: 'event',\n description:\n 'Pipeline lifecycle events. One event per pipeline covering created_at to finished_at (or updated_at if not yet finished), tagged with the terminal status.',\n endpoint: 'GET /api/v4/projects/{id}/pipelines',\n notes:\n 'Derived from the same pipelines response that builds the `pipeline` resource; the GitLab API does not expose an intermediate state-transition history endpoint.',\n },\n issue: {\n shape: 'entity',\n description:\n 'Open and closed issues with labels, author, assignees, and close timestamp.',\n endpoint: 'GET /api/v4/projects/{id}/issues',\n responses: { issues: issuesResponseSchema },\n },\n release: {\n shape: 'entity',\n description:\n 'Project releases keyed by tag name, including released_at and the publishing author.',\n endpoint: 'GET /api/v4/projects/{id}/releases',\n responses: { releases: releasesResponseSchema },\n },\n});\n\nexport const id = 'gitlab';\n\ninterface ProjectBatch<T> {\n projectId: number;\n items: T[];\n}\n\nexport class GitLabConnector extends BaseConnector<\n GitLabSettings,\n GitLabCredentials\n> {\n static readonly id = id;\n\n static readonly resources = gitlabResources;\n\n static readonly schemas = schemasFromResources(gitlabResources);\n\n static create(input: unknown, ctx?: ConnectorContext): GitLabConnector {\n const parsed = configFields.parse(input);\n return new GitLabConnector(\n {\n host: parsed.host ?? DEFAULT_HOST,\n projectIds: parsed.projectIds,\n groupIds: parsed.groupIds,\n resources: parsed.resources,\n },\n { apiToken: parsed.apiToken },\n ctx,\n );\n }\n\n readonly id = id;\n override readonly credentials = gitlabCredentials;\n\n private effectiveProjectIds: number[] | null = null;\n private projectMetadataCache = new Map<number, GitLabProject>();\n\n constructor(\n settings: GitLabSettings,\n creds?: { apiToken: { $secret: string } | string },\n ctx?: ConnectorContext,\n ) {\n super({ ...settings, host: settings.host || DEFAULT_HOST }, creds, ctx);\n }\n\n private buildHeaders(): Record<string, string> {\n return {\n 'PRIVATE-TOKEN': this.creds.apiToken,\n Accept: 'application/json',\n 'User-Agent': connectorUserAgent('gitlab'),\n };\n }\n\n private apiBase(): string {\n return `https://${this.settings.host}/api/v4`;\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 rateLimit: gitlabRateLimit,\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 !== this.settings.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 GitLabPhase,\n readonly GitLabResource[]\n > = {\n projects: ['project'],\n merge_requests: ['merge_request'],\n pipelines: ['pipeline', 'pipeline_event'],\n issues: ['issue'],\n releases: ['release'],\n };\n\n private activePhases(\n optionsResources: ReadonlySet<string> | undefined,\n ): GitLabPhase[] {\n const fromSettings = selectActivePhases<GitLabResource, GitLabPhase>(\n (r) => {\n switch (r) {\n case 'project':\n return 'projects';\n case 'merge_request':\n return 'merge_requests';\n case 'pipeline':\n case 'pipeline_event':\n return 'pipelines';\n case 'issue':\n return 'issues';\n case 'release':\n return 'releases';\n }\n },\n PHASE_ORDER,\n this.settings.resources,\n );\n if (optionsResources === undefined) {\n return fromSettings;\n }\n return fromSettings.filter((phase) =>\n GitLabConnector.PHASE_RESOURCES[phase].some((r) =>\n optionsResources.has(r),\n ),\n );\n }\n\n private isResourceAllowed(\n resource: GitLabResource,\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 private async resolveEffectiveProjectIds(\n signal: AbortSignal | undefined,\n ): Promise<number[]> {\n if (this.effectiveProjectIds !== null) {\n return this.effectiveProjectIds;\n }\n const seen = new Set<number>();\n const ordered: number[] = [];\n const addId = (n: number) => {\n if (!seen.has(n)) {\n seen.add(n);\n ordered.push(n);\n }\n };\n for (const pid of this.settings.projectIds ?? []) {\n addId(pid);\n }\n for (const gid of this.settings.groupIds ?? []) {\n const projects = await this.fetchGroupProjects(gid, signal);\n for (const p of projects) {\n this.projectMetadataCache.set(p.id, p);\n addId(p.id);\n }\n }\n ordered.sort((a, b) => a - b);\n this.effectiveProjectIds = ordered;\n return ordered;\n }\n\n private async fetchGroupProjects(\n groupId: number,\n signal: AbortSignal | undefined,\n ): Promise<GitLabProject[]> {\n const out: GitLabProject[] = [];\n const baseUrl = `${this.apiBase()}/groups/${groupId}/projects`;\n let url: string | null =\n `${baseUrl}?per_page=${PAGE_SIZE}&include_subgroups=true&archived=false`;\n const expectedPath = `/api/v4/groups/${groupId}/projects`;\n while (url !== null) {\n const res = await this.fetch<GitLabProject[]>(\n url,\n 'group_projects',\n signal,\n );\n for (const project of res.body) {\n out.push(project);\n }\n const next = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n url = this.sanitizeUrl(next, expectedPath);\n }\n return out;\n }\n\n private async fetchProjectMetadata(\n projectId: number,\n signal: AbortSignal | undefined,\n ): Promise<GitLabProject | null> {\n if (this.projectMetadataCache.has(projectId)) {\n return this.projectMetadataCache.get(projectId)!;\n }\n const url = `${this.apiBase()}/projects/${projectId}`;\n const res = await this.fetch<GitLabProject>(url, 'project', signal);\n this.projectMetadataCache.set(projectId, res.body);\n return res.body;\n }\n\n private async fetchProjectsPhase(\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const projects = await this.resolveEffectiveProjectIds(signal);\n if (projects.length === 0) {\n return { items: [], next: null };\n }\n const { idx } = decodePage(page);\n if (idx >= projects.length) {\n return { items: [], next: null };\n }\n const projectId = projects[idx]!;\n const project = await this.fetchProjectMetadata(projectId, signal);\n const nextIdx = idx + 1;\n const next = nextIdx < projects.length ? encodePage(nextIdx, null) : null;\n return { items: project ? [project] : [], next };\n }\n\n private buildListPageUrl(\n projectId: number,\n resource: 'merge_requests' | 'pipelines' | 'issues' | 'releases',\n options: SyncOptions,\n ): string {\n const u = new URL(`${this.apiBase()}/projects/${projectId}/${resource}`);\n u.searchParams.set('per_page', String(PAGE_SIZE));\n switch (resource) {\n case 'merge_requests':\n u.searchParams.set('state', 'all');\n u.searchParams.set('order_by', 'updated_at');\n u.searchParams.set('sort', 'desc');\n u.searchParams.set('scope', 'all');\n if (options.since) {\n u.searchParams.set('updated_after', options.since);\n }\n break;\n case 'pipelines':\n u.searchParams.set('order_by', 'updated_at');\n u.searchParams.set('sort', 'desc');\n if (options.since) {\n u.searchParams.set('updated_after', options.since);\n }\n break;\n case 'issues':\n u.searchParams.set('state', 'all');\n u.searchParams.set('order_by', 'updated_at');\n u.searchParams.set('sort', 'desc');\n u.searchParams.set('scope', 'all');\n if (options.since) {\n u.searchParams.set('updated_after', options.since);\n }\n break;\n case 'releases':\n u.searchParams.set('order_by', 'released_at');\n u.searchParams.set('sort', 'desc');\n break;\n }\n return u.toString();\n }\n\n private async fetchListPhase<T>(\n options: SyncOptions,\n page: string | null,\n signal: AbortSignal | undefined,\n resource: 'merge_requests' | 'pipelines' | 'issues' | 'releases',\n rowUpdatedAt: (row: T) => number,\n ): Promise<FetchPageResult<string>> {\n const projects = await this.resolveEffectiveProjectIds(signal);\n if (projects.length === 0) {\n return { items: [], next: null };\n }\n const { idx, url: rawPageUrl } = decodePage(page);\n if (idx >= projects.length) {\n return { items: [], next: null };\n }\n const projectId = projects[idx]!;\n const expectedPath = `/api/v4/projects/${projectId}/${resource}`;\n const fetchUrl =\n this.sanitizeUrl(rawPageUrl, expectedPath) ??\n this.buildListPageUrl(projectId, resource, options);\n const res = await this.fetch<T[]>(fetchUrl, resource, signal);\n const rawNext = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n const safeNext = this.sanitizeUrl(rawNext, expectedPath);\n const rows = res.body;\n\n const cutoff = options.since ? new Date(options.since).getTime() : null;\n let filtered: T[];\n let cutoffReached: boolean;\n if (cutoff !== null) {\n filtered = rows.filter((row) => rowUpdatedAt(row) >= cutoff);\n const last = rows.at(-1);\n cutoffReached = last !== undefined && rowUpdatedAt(last) < cutoff;\n } else {\n filtered = rows;\n cutoffReached = false;\n }\n\n const nextWithinProject = cutoffReached ? null : safeNext;\n const batch: ProjectBatch<T> = { projectId, items: filtered };\n if (nextWithinProject !== null) {\n return { items: [batch], next: encodePage(idx, nextWithinProject) };\n }\n const nextIdx = idx + 1;\n const next = nextIdx < projects.length ? encodePage(nextIdx, null) : null;\n return { items: [batch], next };\n }\n\n private async writeProjects(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n ): Promise<void> {\n if (page === null) {\n await storage.entities([], { types: ['project'] });\n }\n const projects = items as GitLabProject[];\n for (const project of projects) {\n const updatedAt = new Date(\n project.last_activity_at ?? project.created_at,\n ).getTime();\n await storage.entity({\n type: 'project',\n id: String(project.id),\n attributes: {\n name: project.name,\n path_with_namespace: project.path_with_namespace,\n default_branch: project.default_branch ?? '',\n web_url: project.web_url,\n visibility: project.visibility ?? '',\n archived: project.archived ?? false,\n created_at: new Date(project.created_at).getTime(),\n },\n updated_at: updatedAt,\n });\n }\n }\n\n private async writeMergeRequests(\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: ['merge_request'] });\n }\n const batches = items as ProjectBatch<GitLabMergeRequest>[];\n for (const batch of batches) {\n for (const mr of batch.items) {\n await storage.entity({\n type: 'merge_request',\n id: `${batch.projectId}:${mr.iid}`,\n attributes: {\n project_id: batch.projectId,\n iid: mr.iid,\n title: mr.title,\n state: mr.state,\n draft: mr.draft ?? mr.work_in_progress ?? false,\n author: mr.author?.username ?? '',\n assignees: (mr.assignees ?? []).map((a) => a.username),\n source_branch: mr.source_branch,\n target_branch: mr.target_branch,\n web_url: mr.web_url,\n created_at: new Date(mr.created_at).getTime(),\n merged_at: mr.merged_at ? new Date(mr.merged_at).getTime() : null,\n closed_at: mr.closed_at ? new Date(mr.closed_at).getTime() : null,\n },\n updated_at: new Date(mr.updated_at).getTime(),\n });\n }\n }\n }\n\n private async writePipelines(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n options: SyncOptions,\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 ProjectBatch<GitLabPipeline>[];\n const eventsById = new Map<string, Event>();\n for (const batch of batches) {\n for (const pipeline of batch.items) {\n const createdMs = new Date(pipeline.created_at).getTime();\n const updatedMs = new Date(pipeline.updated_at).getTime();\n const finishedMs = pipeline.finished_at\n ? new Date(pipeline.finished_at).getTime()\n : null;\n const durationMs =\n pipeline.duration !== null && pipeline.duration !== undefined\n ? Math.round(pipeline.duration * 1000)\n : finishedMs !== null\n ? finishedMs - createdMs\n : null;\n if (pipelineAllowed) {\n await storage.entity({\n type: 'pipeline',\n id: `${batch.projectId}:${pipeline.id}`,\n attributes: {\n project_id: batch.projectId,\n pipeline_id: pipeline.id,\n status: pipeline.status,\n ref: pipeline.ref ?? '',\n sha: pipeline.sha,\n source: pipeline.source ?? '',\n web_url: pipeline.web_url,\n created_at: createdMs,\n finished_at: finishedMs,\n duration_ms: durationMs,\n },\n updated_at: updatedMs,\n });\n }\n if (eventAllowed) {\n eventsById.set(`${batch.projectId}:${pipeline.id}`, {\n name: 'pipeline_event',\n start_ts: createdMs,\n end_ts: finishedMs ?? updatedMs,\n attributes: {\n project_id: batch.projectId,\n pipeline_id: pipeline.id,\n status: pipeline.status,\n ref: pipeline.ref ?? '',\n sha: pipeline.sha,\n source: pipeline.source ?? '',\n duration_ms: durationMs,\n },\n });\n }\n }\n }\n for (const event of eventsById.values()) {\n await storage.event(event);\n }\n }\n\n private async writeIssues(\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: ['issue'] });\n }\n const batches = items as ProjectBatch<GitLabIssue>[];\n for (const batch of batches) {\n for (const issue of batch.items) {\n await storage.entity({\n type: 'issue',\n id: `${batch.projectId}:${issue.iid}`,\n attributes: {\n project_id: batch.projectId,\n iid: issue.iid,\n title: issue.title,\n state: issue.state,\n labels: issue.labels,\n author: issue.author?.username ?? '',\n assignees: (issue.assignees ?? []).map((a) => a.username),\n web_url: issue.web_url,\n created_at: new Date(issue.created_at).getTime(),\n closed_at: issue.closed_at\n ? new Date(issue.closed_at).getTime()\n : null,\n },\n updated_at: new Date(issue.updated_at).getTime(),\n });\n }\n }\n }\n\n private async writeReleases(\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: ['release'] });\n }\n const batches = items as ProjectBatch<GitLabRelease>[];\n for (const batch of batches) {\n for (const release of batch.items) {\n const createdMs = new Date(release.created_at).getTime();\n const releasedMs = release.released_at\n ? new Date(release.released_at).getTime()\n : null;\n await storage.entity({\n type: 'release',\n id: `${batch.projectId}:${release.tag_name}`,\n attributes: {\n project_id: batch.projectId,\n tag_name: release.tag_name,\n name: release.name ?? '',\n description: release.description ?? '',\n author: release.author?.username ?? '',\n created_at: createdMs,\n released_at: releasedMs,\n },\n updated_at: releasedMs ?? createdMs,\n });\n }\n }\n }\n\n private resolveCursor(cursor: unknown): GitLabSyncCursor | undefined {\n if (!isGitLabSyncCursor(cursor)) {\n return undefined;\n }\n return { phase: cursor.phase, page: cursor.page };\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 return paginateChunked<GitLabPhase, string>({\n phases,\n cursor,\n signal,\n logger: this.logger,\n fetchPage: async (phase, page, sig) => {\n switch (phase) {\n case 'projects':\n return this.fetchProjectsPhase(page, sig);\n case 'merge_requests':\n return this.fetchListPhase<GitLabMergeRequest>(\n options,\n page,\n sig,\n 'merge_requests',\n (mr) => new Date(mr.updated_at).getTime(),\n );\n case 'pipelines':\n return this.fetchListPhase<GitLabPipeline>(\n options,\n page,\n sig,\n 'pipelines',\n (p) => new Date(p.updated_at).getTime(),\n );\n case 'issues':\n return this.fetchListPhase<GitLabIssue>(\n options,\n page,\n sig,\n 'issues',\n (i) => new Date(i.updated_at).getTime(),\n );\n case 'releases':\n return this.fetchListPhase<GitLabRelease>(\n options,\n page,\n sig,\n 'releases',\n (r) => new Date(r.released_at ?? r.created_at).getTime(),\n );\n }\n },\n writeBatch: async (phase, items, page) => {\n switch (phase) {\n case 'projects':\n return this.writeProjects(storage, items, page);\n case 'merge_requests':\n return this.writeMergeRequests(storage, items, page, options);\n case 'pipelines':\n return this.writePipelines(storage, items, page, options);\n case 'issues':\n return this.writeIssues(storage, items, page, options);\n case 'releases':\n return this.writeReleases(storage, items, page, options);\n }\n },\n });\n }\n}\n","import { GitLabConnector } from './gitlab';\n\nexport {\n configFields,\n doc,\n GitLabConnector,\n gitlabResources as resources,\n id,\n} from './gitlab';\nexport type { GitLabResource, GitLabSettings } from './gitlab';\nexport default GitLabConnector;\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/gitlab.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 parseLinkHeader,\n standardRateLimitPolicy,\n} from '@rawdash/connector-shared';\nimport {\n BaseConnector,\n type ChunkedSyncCursor,\n type ConnectorContext,\n type ConnectorDoc,\n type CredentialsSchema,\n type Event,\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\nconst positiveInt = z.number().int().positive();\n\nexport const configFields = defineConfigFields(\n z\n .object({\n apiToken: z.object({ $secret: z.string() }).meta({\n label: 'API Token',\n description:\n 'GitLab Personal Access Token with `read_api` scope. Create one at GitLab -> Preferences -> Access Tokens.',\n placeholder: 'glpat-...',\n secret: true,\n }),\n host: z\n .string()\n .min(1)\n .regex(\n /^[^/\\s:?#]+$/,\n 'Use host only (no protocol, port, path, or query).',\n )\n .optional()\n .meta({\n label: 'Host (optional)',\n description:\n 'Your GitLab host. Defaults to `gitlab.com`. For self-hosted, supply the hostname only (e.g. `gitlab.example.com`).',\n placeholder: 'gitlab.com',\n }),\n projectIds: z.array(positiveInt).nonempty().optional().meta({\n label: 'Project IDs (optional)',\n description:\n 'Numeric project IDs to sync directly (find one in Project -> Settings -> General). Combined with any projects discovered via `groupIds`.',\n }),\n groupIds: z.array(positiveInt).nonempty().optional().meta({\n label: 'Group IDs (optional)',\n description:\n 'Numeric group IDs whose projects (including subgroups) will be discovered and synced.',\n }),\n resources: z\n .array(\n z.enum([\n 'project',\n 'merge_request',\n 'pipeline',\n 'pipeline_event',\n 'issue',\n 'release',\n ]),\n )\n .nonempty()\n .optional()\n .meta({\n label: 'Resources',\n description:\n \"Which GitLab 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 .refine(\n (v) =>\n (v.projectIds && v.projectIds.length > 0) ||\n (v.groupIds && v.groupIds.length > 0),\n {\n message: 'At least one of `projectIds` or `groupIds` must be provided.',\n path: ['projectIds'],\n },\n ),\n);\n\nexport const doc: ConnectorDoc = defineConnectorDoc({\n displayName: 'GitLab',\n category: 'engineering',\n brandColor: '#FC6D26',\n tagline:\n 'Sync projects, merge requests, pipelines, issues, and releases from GitLab.com or a self-hosted GitLab instance.',\n vendor: {\n name: 'GitLab',\n domain: 'gitlab.com',\n apiDocs: 'https://docs.gitlab.com/ee/api/',\n website: 'https://gitlab.com',\n },\n auth: {\n summary:\n 'A GitLab Personal Access Token (PAT) with the `read_api` scope is required. The PAT must belong to an account with read access to the projects and groups you want to sync. Self-hosted GitLab is supported by overriding the `host` field.',\n setup: [\n 'Open GitLab -> User Preferences -> Access Tokens (or the equivalent on your self-hosted instance).',\n 'Create a Personal Access Token with the `read_api` scope.',\n 'Store it as a secret and reference it from the connector config as `apiToken: secret(\"GITLAB_API_TOKEN\")`.',\n 'Set `projectIds` to a list of numeric project IDs, or `groupIds` to a list of numeric group IDs (or both). At least one must be set.',\n 'For self-hosted GitLab, set `host` to your instance hostname (no protocol or path), e.g. `gitlab.example.com`.',\n ],\n },\n rateLimit:\n 'GitLab returns standard `RateLimit-Remaining` / `RateLimit-Reset` headers (reset is a Unix timestamp in seconds); list pagination uses the Link header (page size 100).',\n limitations: [\n 'Container Registry, Packages, and GitLab Duo / AI features are out of scope.',\n 'Pipeline state-transition events are synthesized: one `pipeline_event` is emitted per pipeline lifecycle (created_at to finished_at/updated_at), not one per intermediate state change.',\n 'Group project discovery walks each group with `include_subgroups=true`; very large groups may take multiple sync chunks to enumerate.',\n ],\n});\n\nexport type GitLabResource =\n | 'project'\n | 'merge_request'\n | 'pipeline'\n | 'pipeline_event'\n | 'issue'\n | 'release';\n\nexport interface GitLabSettings {\n host: string;\n projectIds?: readonly number[];\n groupIds?: readonly number[];\n resources?: readonly GitLabResource[];\n}\n\nconst gitlabCredentials = {\n apiToken: {\n description: 'GitLab Personal Access Token',\n auth: 'required' as const,\n },\n} satisfies CredentialsSchema;\n\ntype GitLabCredentials = typeof gitlabCredentials;\n\nconst DEFAULT_HOST = 'gitlab.com';\nconst PAGE_SIZE = 100;\n\nconst gitlabRateLimit = standardRateLimitPolicy({\n remainingHeader: 'ratelimit-remaining',\n resetHeader: 'ratelimit-reset',\n resetUnit: 's',\n});\n\nconst PHASE_ORDER = [\n 'projects',\n 'merge_requests',\n 'pipelines',\n 'issues',\n 'releases',\n] as const;\n\ntype GitLabPhase = (typeof PHASE_ORDER)[number];\n\ntype GitLabSyncCursor = ChunkedSyncCursor<GitLabPhase, string>;\n\nconst isGitLabSyncCursor = makeChunkedCursorGuard(PHASE_ORDER);\n\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\ninterface GitLabUserRef {\n id: number;\n username: string;\n name?: string | null;\n}\n\ninterface GitLabProject {\n id: number;\n name: string;\n path_with_namespace: string;\n default_branch: string | null;\n web_url: string;\n created_at: string;\n last_activity_at?: string | null;\n archived?: boolean;\n visibility?: string;\n}\n\ninterface GitLabMergeRequest {\n id: number;\n iid: number;\n project_id: number;\n title: string;\n state: string;\n draft?: boolean;\n work_in_progress?: boolean;\n author: GitLabUserRef | null;\n assignees?: GitLabUserRef[];\n source_branch: string;\n target_branch: string;\n created_at: string;\n updated_at: string;\n merged_at: string | null;\n closed_at: string | null;\n web_url: string;\n}\n\ninterface GitLabPipeline {\n id: number;\n iid?: number;\n project_id: number;\n status: string;\n ref: string | null;\n sha: string;\n source: string | null;\n created_at: string;\n updated_at: string;\n started_at?: string | null;\n finished_at?: string | null;\n duration?: number | null;\n web_url: string;\n}\n\ninterface GitLabIssue {\n id: number;\n iid: number;\n project_id: number;\n title: string;\n state: string;\n labels: string[];\n author: GitLabUserRef | null;\n assignees?: GitLabUserRef[];\n created_at: string;\n updated_at: string;\n closed_at: string | null;\n web_url: string;\n}\n\ninterface GitLabRelease {\n tag_name: string;\n name: string | null;\n description?: string | null;\n created_at: string;\n released_at: string | null;\n author?: GitLabUserRef | null;\n}\n\nconst userRefSchema = z.object({\n id: z.number().int(),\n username: z.string().min(1),\n name: z.string().nullable().optional(),\n});\n\nconst projectSchema = z.object({\n id: z.number().int(),\n name: z.string().min(1),\n path_with_namespace: z.string().min(1),\n default_branch: z.string().nullable(),\n web_url: z.string(),\n created_at: z.iso.datetime(),\n last_activity_at: z.iso.datetime().nullable().optional(),\n archived: z.boolean().optional(),\n visibility: z.string().optional(),\n});\n\nconst projectsResponseSchema = z.array(projectSchema);\n\nconst mergeRequestSchema = z.object({\n id: z.number().int(),\n iid: z.number().int(),\n project_id: z.number().int(),\n title: z.string(),\n state: z.string().min(1),\n draft: z.boolean().optional(),\n work_in_progress: z.boolean().optional(),\n author: userRefSchema.nullable(),\n assignees: z.array(userRefSchema).optional(),\n source_branch: z.string(),\n target_branch: z.string(),\n created_at: z.iso.datetime(),\n updated_at: z.iso.datetime(),\n merged_at: z.iso.datetime().nullable(),\n closed_at: z.iso.datetime().nullable(),\n web_url: z.string(),\n});\n\nconst mergeRequestsResponseSchema = z.array(mergeRequestSchema);\n\nconst pipelineSchema = z.object({\n id: z.number().int(),\n iid: z.number().int().optional(),\n project_id: z.number().int(),\n status: z.string().min(1),\n ref: z.string().nullable(),\n sha: z.string().min(1),\n source: z.string().nullable(),\n created_at: z.iso.datetime(),\n updated_at: z.iso.datetime(),\n started_at: z.iso.datetime().nullable().optional(),\n finished_at: z.iso.datetime().nullable().optional(),\n duration: z.number().nullable().optional(),\n web_url: z.string(),\n});\n\nconst pipelinesResponseSchema = z.array(pipelineSchema);\n\nconst issueSchema = z.object({\n id: z.number().int(),\n iid: z.number().int(),\n project_id: z.number().int(),\n title: z.string(),\n state: z.string().min(1),\n labels: z.array(z.string()),\n author: userRefSchema.nullable(),\n assignees: z.array(userRefSchema).optional(),\n created_at: z.iso.datetime(),\n updated_at: z.iso.datetime(),\n closed_at: z.iso.datetime().nullable(),\n web_url: z.string(),\n});\n\nconst issuesResponseSchema = z.array(issueSchema);\n\nconst releaseSchema = z.object({\n tag_name: z.string().min(1),\n name: z.string().nullable(),\n description: z.string().nullable().optional(),\n created_at: z.iso.datetime(),\n released_at: z.iso.datetime().nullable(),\n author: userRefSchema.nullable().optional(),\n});\n\nconst releasesResponseSchema = z.array(releaseSchema);\n\nexport const gitlabResources = defineResources({\n project: {\n shape: 'entity',\n description:\n 'GitLab projects (repositories) with namespace path, default branch, and archived/visibility flags.',\n endpoint: 'GET /api/v4/projects/{id}',\n notes:\n 'Discovered from configured `projectIds` and from `groupIds` via GET /api/v4/groups/{id}/projects?include_subgroups=true.',\n filterable: [],\n responses: { projects: projectsResponseSchema },\n },\n merge_request: {\n shape: 'entity',\n description:\n 'Open, merged, and closed merge requests with author, source/target branches, and merge timestamps.',\n endpoint: 'GET /api/v4/projects/{id}/merge_requests',\n filterable: [\n {\n field: 'state',\n ops: ['eq'],\n values: ['opened', 'closed', 'merged', 'locked'],\n },\n ],\n responses: { merge_requests: mergeRequestsResponseSchema },\n },\n pipeline: {\n shape: 'entity',\n description:\n 'CI/CD pipelines with status, ref, commit sha, source, duration, and start/finish timestamps.',\n endpoint: 'GET /api/v4/projects/{id}/pipelines',\n filterable: [{ field: 'status', ops: ['eq'] }],\n responses: { pipelines: pipelinesResponseSchema },\n },\n pipeline_event: {\n shape: 'event',\n description:\n 'Pipeline lifecycle events. One event per pipeline covering created_at to finished_at (or updated_at if not yet finished), tagged with the terminal status.',\n endpoint: 'GET /api/v4/projects/{id}/pipelines',\n notes:\n 'Derived from the same pipelines response that builds the `pipeline` resource; the GitLab API does not expose an intermediate state-transition history endpoint.',\n filterable: [],\n },\n issue: {\n shape: 'entity',\n description:\n 'Open and closed issues with labels, author, assignees, and close timestamp.',\n endpoint: 'GET /api/v4/projects/{id}/issues',\n filterable: [{ field: 'state', ops: ['eq'], values: ['opened', 'closed'] }],\n responses: { issues: issuesResponseSchema },\n },\n release: {\n shape: 'entity',\n description:\n 'Project releases keyed by tag name, including released_at and the publishing author.',\n endpoint: 'GET /api/v4/projects/{id}/releases',\n filterable: [],\n responses: { releases: releasesResponseSchema },\n },\n});\n\nexport const id = 'gitlab';\n\nconst LIST_RESOURCE_DEF_KEY: Record<\n 'merge_requests' | 'pipelines' | 'issues' | 'releases',\n string\n> = {\n merge_requests: 'merge_request',\n pipelines: 'pipeline',\n issues: 'issue',\n releases: 'release',\n};\n\nfunction pushableEq(\n filter: FilterClause[] | undefined,\n field: string,\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 === field &&\n clause.op === 'eq' &&\n typeof clause.value === 'string'\n ) {\n return clause.value;\n }\n }\n return null;\n}\n\ninterface ProjectBatch<T> {\n projectId: number;\n items: T[];\n}\n\nexport class GitLabConnector extends BaseConnector<\n GitLabSettings,\n GitLabCredentials\n> {\n static readonly id = id;\n\n static readonly resources = gitlabResources;\n\n static readonly schemas = schemasFromResources(gitlabResources);\n\n static create(input: unknown, ctx?: ConnectorContext): GitLabConnector {\n const parsed = configFields.parse(input);\n return new GitLabConnector(\n {\n host: parsed.host ?? DEFAULT_HOST,\n projectIds: parsed.projectIds,\n groupIds: parsed.groupIds,\n resources: parsed.resources,\n },\n { apiToken: parsed.apiToken },\n ctx,\n );\n }\n\n readonly id = id;\n override readonly credentials = gitlabCredentials;\n\n private effectiveProjectIds: number[] | null = null;\n private projectMetadataCache = new Map<number, GitLabProject>();\n\n constructor(\n settings: GitLabSettings,\n creds?: { apiToken: { $secret: string } | string },\n ctx?: ConnectorContext,\n ) {\n super({ ...settings, host: settings.host || DEFAULT_HOST }, creds, ctx);\n }\n\n private buildHeaders(): Record<string, string> {\n return {\n 'PRIVATE-TOKEN': this.creds.apiToken,\n Accept: 'application/json',\n 'User-Agent': connectorUserAgent('gitlab'),\n };\n }\n\n private apiBase(): string {\n return `https://${this.settings.host}/api/v4`;\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 rateLimit: gitlabRateLimit,\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 !== this.settings.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 GitLabPhase,\n readonly GitLabResource[]\n > = {\n projects: ['project'],\n merge_requests: ['merge_request'],\n pipelines: ['pipeline', 'pipeline_event'],\n issues: ['issue'],\n releases: ['release'],\n };\n\n private activePhases(\n optionsResources: ReadonlySet<string> | undefined,\n ): GitLabPhase[] {\n const fromSettings = selectActivePhases<GitLabResource, GitLabPhase>(\n (r) => {\n switch (r) {\n case 'project':\n return 'projects';\n case 'merge_request':\n return 'merge_requests';\n case 'pipeline':\n case 'pipeline_event':\n return 'pipelines';\n case 'issue':\n return 'issues';\n case 'release':\n return 'releases';\n }\n },\n PHASE_ORDER,\n this.settings.resources,\n );\n if (optionsResources === undefined) {\n return fromSettings;\n }\n return fromSettings.filter((phase) =>\n GitLabConnector.PHASE_RESOURCES[phase].some((r) =>\n optionsResources.has(r),\n ),\n );\n }\n\n private isResourceAllowed(\n resource: GitLabResource,\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 private async resolveEffectiveProjectIds(\n signal: AbortSignal | undefined,\n ): Promise<number[]> {\n if (this.effectiveProjectIds !== null) {\n return this.effectiveProjectIds;\n }\n const seen = new Set<number>();\n const ordered: number[] = [];\n const addId = (n: number) => {\n if (!seen.has(n)) {\n seen.add(n);\n ordered.push(n);\n }\n };\n for (const pid of this.settings.projectIds ?? []) {\n addId(pid);\n }\n for (const gid of this.settings.groupIds ?? []) {\n const projects = await this.fetchGroupProjects(gid, signal);\n for (const p of projects) {\n this.projectMetadataCache.set(p.id, p);\n addId(p.id);\n }\n }\n ordered.sort((a, b) => a - b);\n this.effectiveProjectIds = ordered;\n return ordered;\n }\n\n private async fetchGroupProjects(\n groupId: number,\n signal: AbortSignal | undefined,\n ): Promise<GitLabProject[]> {\n const out: GitLabProject[] = [];\n const baseUrl = `${this.apiBase()}/groups/${groupId}/projects`;\n let url: string | null =\n `${baseUrl}?per_page=${PAGE_SIZE}&include_subgroups=true&archived=false`;\n const expectedPath = `/api/v4/groups/${groupId}/projects`;\n while (url !== null) {\n const res = await this.fetch<GitLabProject[]>(\n url,\n 'group_projects',\n signal,\n );\n for (const project of res.body) {\n out.push(project);\n }\n const next = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n url = this.sanitizeUrl(next, expectedPath);\n }\n return out;\n }\n\n private async fetchProjectMetadata(\n projectId: number,\n signal: AbortSignal | undefined,\n ): Promise<GitLabProject | null> {\n if (this.projectMetadataCache.has(projectId)) {\n return this.projectMetadataCache.get(projectId)!;\n }\n const url = `${this.apiBase()}/projects/${projectId}`;\n const res = await this.fetch<GitLabProject>(url, 'project', signal);\n this.projectMetadataCache.set(projectId, res.body);\n return res.body;\n }\n\n private async fetchProjectsPhase(\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const projects = await this.resolveEffectiveProjectIds(signal);\n if (projects.length === 0) {\n return { items: [], next: null };\n }\n const { idx } = decodePage(page);\n if (idx >= projects.length) {\n return { items: [], next: null };\n }\n const projectId = projects[idx]!;\n const project = await this.fetchProjectMetadata(projectId, signal);\n const nextIdx = idx + 1;\n const next = nextIdx < projects.length ? encodePage(nextIdx, null) : null;\n return { items: project ? [project] : [], next };\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 buildListPageUrl(\n projectId: number,\n resource: 'merge_requests' | 'pipelines' | 'issues' | 'releases',\n options: SyncOptions,\n spec?: FetchSpec,\n ): string {\n const u = new URL(`${this.apiBase()}/projects/${projectId}/${resource}`);\n u.searchParams.set('per_page', String(PAGE_SIZE));\n switch (resource) {\n case 'merge_requests':\n u.searchParams.set('state', pushableEq(spec?.filter, 'state') ?? 'all');\n u.searchParams.set('order_by', 'updated_at');\n u.searchParams.set('sort', 'desc');\n u.searchParams.set('scope', 'all');\n if (options.since) {\n u.searchParams.set('updated_after', options.since);\n }\n break;\n case 'pipelines': {\n const status = pushableEq(spec?.filter, 'status');\n if (status !== null) {\n u.searchParams.set('status', status);\n }\n u.searchParams.set('order_by', 'updated_at');\n u.searchParams.set('sort', 'desc');\n if (options.since) {\n u.searchParams.set('updated_after', options.since);\n }\n break;\n }\n case 'issues':\n u.searchParams.set('state', pushableEq(spec?.filter, 'state') ?? 'all');\n u.searchParams.set('order_by', 'updated_at');\n u.searchParams.set('sort', 'desc');\n u.searchParams.set('scope', 'all');\n if (options.since) {\n u.searchParams.set('updated_after', options.since);\n }\n break;\n case 'releases':\n u.searchParams.set('order_by', 'released_at');\n u.searchParams.set('sort', 'desc');\n break;\n }\n return u.toString();\n }\n\n private async fetchListPhase<T>(\n options: SyncOptions,\n page: string | null,\n signal: AbortSignal | undefined,\n resource: 'merge_requests' | 'pipelines' | 'issues' | 'releases',\n rowUpdatedAt: (row: T) => number,\n ): Promise<FetchPageResult<string>> {\n const projects = await this.resolveEffectiveProjectIds(signal);\n if (projects.length === 0) {\n return { items: [], next: null };\n }\n const { idx, url: rawPageUrl } = decodePage(page);\n if (idx >= projects.length) {\n return { items: [], next: null };\n }\n const projectId = projects[idx]!;\n const expectedPath = `/api/v4/projects/${projectId}/${resource}`;\n const fetchUrl =\n this.sanitizeUrl(rawPageUrl, expectedPath) ??\n this.buildListPageUrl(\n projectId,\n resource,\n options,\n this.singleSpec(options, LIST_RESOURCE_DEF_KEY[resource]),\n );\n const res = await this.fetch<T[]>(fetchUrl, resource, signal);\n const rawNext = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n const safeNext = this.sanitizeUrl(rawNext, expectedPath);\n const rows = res.body;\n\n const cutoff = options.since ? new Date(options.since).getTime() : null;\n let filtered: T[];\n let cutoffReached: boolean;\n if (cutoff !== null) {\n filtered = rows.filter((row) => rowUpdatedAt(row) >= cutoff);\n const last = rows.at(-1);\n cutoffReached = last !== undefined && rowUpdatedAt(last) < cutoff;\n } else {\n filtered = rows;\n cutoffReached = false;\n }\n\n const nextWithinProject = cutoffReached ? null : safeNext;\n const batch: ProjectBatch<T> = { projectId, items: filtered };\n if (nextWithinProject !== null) {\n return { items: [batch], next: encodePage(idx, nextWithinProject) };\n }\n const nextIdx = idx + 1;\n const next = nextIdx < projects.length ? encodePage(nextIdx, null) : null;\n return { items: [batch], next };\n }\n\n private async writeProjects(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n ): Promise<void> {\n if (page === null) {\n await storage.entities([], { types: ['project'] });\n }\n const projects = items as GitLabProject[];\n for (const project of projects) {\n const updatedAt = new Date(\n project.last_activity_at ?? project.created_at,\n ).getTime();\n await storage.entity({\n type: 'project',\n id: String(project.id),\n attributes: {\n name: project.name,\n path_with_namespace: project.path_with_namespace,\n default_branch: project.default_branch ?? '',\n web_url: project.web_url,\n visibility: project.visibility ?? '',\n archived: project.archived ?? false,\n created_at: new Date(project.created_at).getTime(),\n },\n updated_at: updatedAt,\n });\n }\n }\n\n private async writeMergeRequests(\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: ['merge_request'] });\n }\n const batches = items as ProjectBatch<GitLabMergeRequest>[];\n for (const batch of batches) {\n for (const mr of batch.items) {\n await storage.entity({\n type: 'merge_request',\n id: `${batch.projectId}:${mr.iid}`,\n attributes: {\n project_id: batch.projectId,\n iid: mr.iid,\n title: mr.title,\n state: mr.state,\n draft: mr.draft ?? mr.work_in_progress ?? false,\n author: mr.author?.username ?? '',\n assignees: (mr.assignees ?? []).map((a) => a.username),\n source_branch: mr.source_branch,\n target_branch: mr.target_branch,\n web_url: mr.web_url,\n created_at: new Date(mr.created_at).getTime(),\n merged_at: mr.merged_at ? new Date(mr.merged_at).getTime() : null,\n closed_at: mr.closed_at ? new Date(mr.closed_at).getTime() : null,\n },\n updated_at: new Date(mr.updated_at).getTime(),\n });\n }\n }\n }\n\n private async writePipelines(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n options: SyncOptions,\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 ProjectBatch<GitLabPipeline>[];\n const eventsById = new Map<string, Event>();\n for (const batch of batches) {\n for (const pipeline of batch.items) {\n const createdMs = new Date(pipeline.created_at).getTime();\n const updatedMs = new Date(pipeline.updated_at).getTime();\n const finishedMs = pipeline.finished_at\n ? new Date(pipeline.finished_at).getTime()\n : null;\n const durationMs =\n pipeline.duration !== null && pipeline.duration !== undefined\n ? Math.round(pipeline.duration * 1000)\n : finishedMs !== null\n ? finishedMs - createdMs\n : null;\n if (pipelineAllowed) {\n await storage.entity({\n type: 'pipeline',\n id: `${batch.projectId}:${pipeline.id}`,\n attributes: {\n project_id: batch.projectId,\n pipeline_id: pipeline.id,\n status: pipeline.status,\n ref: pipeline.ref ?? '',\n sha: pipeline.sha,\n source: pipeline.source ?? '',\n web_url: pipeline.web_url,\n created_at: createdMs,\n finished_at: finishedMs,\n duration_ms: durationMs,\n },\n updated_at: updatedMs,\n });\n }\n if (eventAllowed) {\n eventsById.set(`${batch.projectId}:${pipeline.id}`, {\n name: 'pipeline_event',\n start_ts: createdMs,\n end_ts: finishedMs ?? updatedMs,\n attributes: {\n project_id: batch.projectId,\n pipeline_id: pipeline.id,\n status: pipeline.status,\n ref: pipeline.ref ?? '',\n sha: pipeline.sha,\n source: pipeline.source ?? '',\n duration_ms: durationMs,\n },\n });\n }\n }\n }\n for (const event of eventsById.values()) {\n await storage.event(event);\n }\n }\n\n private async writeIssues(\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: ['issue'] });\n }\n const batches = items as ProjectBatch<GitLabIssue>[];\n for (const batch of batches) {\n for (const issue of batch.items) {\n await storage.entity({\n type: 'issue',\n id: `${batch.projectId}:${issue.iid}`,\n attributes: {\n project_id: batch.projectId,\n iid: issue.iid,\n title: issue.title,\n state: issue.state,\n labels: issue.labels,\n author: issue.author?.username ?? '',\n assignees: (issue.assignees ?? []).map((a) => a.username),\n web_url: issue.web_url,\n created_at: new Date(issue.created_at).getTime(),\n closed_at: issue.closed_at\n ? new Date(issue.closed_at).getTime()\n : null,\n },\n updated_at: new Date(issue.updated_at).getTime(),\n });\n }\n }\n }\n\n private async writeReleases(\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: ['release'] });\n }\n const batches = items as ProjectBatch<GitLabRelease>[];\n for (const batch of batches) {\n for (const release of batch.items) {\n const createdMs = new Date(release.created_at).getTime();\n const releasedMs = release.released_at\n ? new Date(release.released_at).getTime()\n : null;\n await storage.entity({\n type: 'release',\n id: `${batch.projectId}:${release.tag_name}`,\n attributes: {\n project_id: batch.projectId,\n tag_name: release.tag_name,\n name: release.name ?? '',\n description: release.description ?? '',\n author: release.author?.username ?? '',\n created_at: createdMs,\n released_at: releasedMs,\n },\n updated_at: releasedMs ?? createdMs,\n });\n }\n }\n }\n\n private resolveCursor(cursor: unknown): GitLabSyncCursor | undefined {\n if (!isGitLabSyncCursor(cursor)) {\n return undefined;\n }\n return { phase: cursor.phase, page: cursor.page };\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 return paginateChunked<GitLabPhase, string>({\n phases,\n cursor,\n signal,\n logger: this.logger,\n fetchPage: async (phase, page, sig) => {\n switch (phase) {\n case 'projects':\n return this.fetchProjectsPhase(page, sig);\n case 'merge_requests':\n return this.fetchListPhase<GitLabMergeRequest>(\n options,\n page,\n sig,\n 'merge_requests',\n (mr) => new Date(mr.updated_at).getTime(),\n );\n case 'pipelines':\n return this.fetchListPhase<GitLabPipeline>(\n options,\n page,\n sig,\n 'pipelines',\n (p) => new Date(p.updated_at).getTime(),\n );\n case 'issues':\n return this.fetchListPhase<GitLabIssue>(\n options,\n page,\n sig,\n 'issues',\n (i) => new Date(i.updated_at).getTime(),\n );\n case 'releases':\n return this.fetchListPhase<GitLabRelease>(\n options,\n page,\n sig,\n 'releases',\n (r) => new Date(r.released_at ?? r.created_at).getTime(),\n );\n }\n },\n writeBatch: async (phase, items, page) => {\n switch (phase) {\n case 'projects':\n return this.writeProjects(storage, items, page);\n case 'merge_requests':\n return this.writeMergeRequests(storage, items, page, options);\n case 'pipelines':\n return this.writePipelines(storage, items, page, options);\n case 'issues':\n return this.writeIssues(storage, items, page, options);\n case 'releases':\n return this.writeReleases(storage, items, page, options);\n }\n },\n });\n }\n}\n","import { GitLabConnector } from './gitlab';\n\nexport {\n configFields,\n doc,\n GitLabConnector,\n gitlabResources as resources,\n id,\n} from './gitlab';\nexport type { GitLabResource, GitLabSettings } from './gitlab';\nexport default GitLabConnector;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rawdash/connector-gitlab",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Rawdash connector for GitLab",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"zod": "^4.4.3",
|
|
27
|
-
"@rawdash/core": "0.
|
|
27
|
+
"@rawdash/core": "0.23.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"fast-check": "^4.8.0",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"typescript": "^5.7.2",
|
|
33
33
|
"vitest": "^4.1.4",
|
|
34
34
|
"@rawdash/connector-shared": "0.3.1",
|
|
35
|
-
"@rawdash/connector-test-utils": "0.0.
|
|
35
|
+
"@rawdash/connector-test-utils": "0.0.10"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "tsup",
|