@rawdash/connector-github 0.7.1 → 0.8.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 +7 -5
- package/dist/index.js +341 -240
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseConnector, SyncOptions, StorageHandle } from '@rawdash/core';
|
|
1
|
+
import { BaseConnector, SyncOptions, StorageHandle, SyncResult } from '@rawdash/core';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
declare const configFields: z.ZodObject<{
|
|
@@ -31,15 +31,17 @@ declare class GitHubActionsConnector extends BaseConnector<GitHubActionsSettings
|
|
|
31
31
|
};
|
|
32
32
|
private buildHeaders;
|
|
33
33
|
private get;
|
|
34
|
-
private
|
|
35
|
-
private
|
|
34
|
+
private syncRepoStats;
|
|
35
|
+
private syncWorkflowRunsLatest;
|
|
36
|
+
private syncWorkflowRunsFull;
|
|
36
37
|
private syncPullRequests;
|
|
37
38
|
private syncIssues;
|
|
38
39
|
private syncDeployments;
|
|
39
40
|
private syncReleases;
|
|
40
41
|
private syncContributors;
|
|
41
|
-
private
|
|
42
|
-
|
|
42
|
+
private sanitizeIncomingCursor;
|
|
43
|
+
private runPhase;
|
|
44
|
+
sync(options: SyncOptions, storage: StorageHandle, signal?: AbortSignal): Promise<SyncResult>;
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
export { GitHubActionsConnector, type GitHubActionsSettings, configFields };
|
package/dist/index.js
CHANGED
|
@@ -275,20 +275,6 @@ function parseLinkHeader(header) {
|
|
|
275
275
|
}
|
|
276
276
|
return result;
|
|
277
277
|
}
|
|
278
|
-
async function* paginateLink(initial, parse) {
|
|
279
|
-
let next = initial.url;
|
|
280
|
-
while (next) {
|
|
281
|
-
const res = await request({
|
|
282
|
-
...initial,
|
|
283
|
-
url: next
|
|
284
|
-
});
|
|
285
|
-
for (const item of parse(res.body)) {
|
|
286
|
-
yield item;
|
|
287
|
-
}
|
|
288
|
-
const links = parseLinkHeader(res.headers.get("link"));
|
|
289
|
-
next = links["next"] ?? null;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
278
|
|
|
293
279
|
// src/github-actions.ts
|
|
294
280
|
import {
|
|
@@ -321,6 +307,31 @@ var githubCredentials = {
|
|
|
321
307
|
auth: "optional"
|
|
322
308
|
}
|
|
323
309
|
};
|
|
310
|
+
var PHASE_ORDER = [
|
|
311
|
+
"repo_stats",
|
|
312
|
+
"workflow_runs",
|
|
313
|
+
"pull_requests",
|
|
314
|
+
"issues",
|
|
315
|
+
"deployments",
|
|
316
|
+
"releases",
|
|
317
|
+
"contributors"
|
|
318
|
+
];
|
|
319
|
+
function isGitHubSyncCursor(value) {
|
|
320
|
+
if (typeof value !== "object" || value === null) {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
const v = value;
|
|
324
|
+
if (typeof v.phase !== "string") {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
if (!PHASE_ORDER.includes(v.phase)) {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
if (v.pageUrl !== void 0 && typeof v.pageUrl !== "string") {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
324
335
|
var GitHubActionsConnector = class _GitHubActionsConnector extends BaseConnector {
|
|
325
336
|
static id = "github-actions";
|
|
326
337
|
static create(input) {
|
|
@@ -352,240 +363,301 @@ var GitHubActionsConnector = class _GitHubActionsConnector extends BaseConnector
|
|
|
352
363
|
};
|
|
353
364
|
return request(req);
|
|
354
365
|
}
|
|
355
|
-
async
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
366
|
+
async syncRepoStats(storage, initialPageUrl, signal) {
|
|
367
|
+
const { owner, repo } = this.settings;
|
|
368
|
+
const url = initialPageUrl ?? `https://api.github.com/repos/${owner}/${repo}`;
|
|
369
|
+
const res = await this.get(url, signal);
|
|
370
|
+
await storage.entities(
|
|
371
|
+
[
|
|
372
|
+
{
|
|
373
|
+
type: "repo",
|
|
374
|
+
id: `${owner}/${repo}`,
|
|
375
|
+
attributes: {
|
|
376
|
+
stars: res.body.stargazers_count,
|
|
377
|
+
forks: res.body.forks_count,
|
|
378
|
+
watchers: res.body.subscribers_count
|
|
379
|
+
},
|
|
380
|
+
updated_at: Date.now()
|
|
381
|
+
}
|
|
382
|
+
],
|
|
383
|
+
{ types: ["repo"] }
|
|
364
384
|
);
|
|
385
|
+
return { done: true };
|
|
365
386
|
}
|
|
366
|
-
async
|
|
387
|
+
async syncWorkflowRunsLatest(storage, signal) {
|
|
367
388
|
const { owner, repo } = this.settings;
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
return;
|
|
389
|
+
const res = await this.get(
|
|
390
|
+
`https://api.github.com/repos/${owner}/${repo}/actions/runs?per_page=1`,
|
|
391
|
+
signal
|
|
392
|
+
);
|
|
393
|
+
const run = res.body.workflow_runs[0];
|
|
394
|
+
if (run) {
|
|
395
|
+
await storage.event({
|
|
396
|
+
name: "workflow_run",
|
|
397
|
+
start_ts: new Date(run.created_at).getTime(),
|
|
398
|
+
end_ts: new Date(run.updated_at).getTime(),
|
|
399
|
+
attributes: {
|
|
400
|
+
id: run.id,
|
|
401
|
+
workflow_name: run.name,
|
|
402
|
+
conclusion: run.conclusion ?? "unknown",
|
|
403
|
+
status: run.status,
|
|
404
|
+
branch: run.head_branch ?? "",
|
|
405
|
+
actor: run.actor?.login ?? "",
|
|
406
|
+
run_attempt: run.run_attempt
|
|
407
|
+
}
|
|
408
|
+
});
|
|
391
409
|
}
|
|
410
|
+
return { done: true };
|
|
411
|
+
}
|
|
412
|
+
async syncWorkflowRunsFull(storage, options, initialPageUrl, signal) {
|
|
413
|
+
const { owner, repo } = this.settings;
|
|
392
414
|
const cutoff = options.since ? new Date(options.since).getTime() : null;
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
401
|
-
(
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
signal?.throwIfAborted();
|
|
406
|
-
const runs = page.workflow_runs;
|
|
415
|
+
if (initialPageUrl === void 0) {
|
|
416
|
+
await storage.events([], { names: ["workflow_run"] });
|
|
417
|
+
}
|
|
418
|
+
let nextUrl = initialPageUrl ?? `https://api.github.com/repos/${owner}/${repo}/actions/runs?per_page=100`;
|
|
419
|
+
while (nextUrl) {
|
|
420
|
+
if (signal?.aborted) {
|
|
421
|
+
return { done: false, pageUrl: nextUrl };
|
|
422
|
+
}
|
|
423
|
+
const res = await this.get(nextUrl, signal);
|
|
424
|
+
const body = res.body;
|
|
425
|
+
const nextLink = parseLinkHeader(res.headers.get("link"))["next"] ?? null;
|
|
426
|
+
const runs = body.workflow_runs;
|
|
407
427
|
if (runs.length === 0) {
|
|
408
|
-
|
|
428
|
+
return { done: true };
|
|
409
429
|
}
|
|
410
|
-
|
|
430
|
+
const pageEvents = runs.filter((run) => {
|
|
431
|
+
if (cutoff === null) {
|
|
432
|
+
return true;
|
|
433
|
+
}
|
|
411
434
|
const createdMs = new Date(run.created_at).getTime();
|
|
412
435
|
const updatedMs = new Date(run.updated_at).getTime();
|
|
413
|
-
|
|
414
|
-
|
|
436
|
+
return !(createdMs < cutoff && updatedMs < cutoff);
|
|
437
|
+
}).map((run) => ({
|
|
438
|
+
name: "workflow_run",
|
|
439
|
+
start_ts: new Date(run.created_at).getTime(),
|
|
440
|
+
end_ts: new Date(run.updated_at).getTime(),
|
|
441
|
+
attributes: {
|
|
442
|
+
id: run.id,
|
|
443
|
+
workflow_name: run.name,
|
|
444
|
+
conclusion: run.conclusion ?? "unknown",
|
|
445
|
+
status: run.status,
|
|
446
|
+
branch: run.head_branch ?? "",
|
|
447
|
+
actor: run.actor?.login ?? "",
|
|
448
|
+
run_attempt: run.run_attempt
|
|
415
449
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
end_ts: updatedMs,
|
|
420
|
-
attributes: {
|
|
421
|
-
id: run.id,
|
|
422
|
-
workflow_name: run.name,
|
|
423
|
-
conclusion: run.conclusion ?? "unknown",
|
|
424
|
-
status: run.status,
|
|
425
|
-
branch: run.head_branch ?? "",
|
|
426
|
-
actor: run.actor?.login ?? "",
|
|
427
|
-
run_attempt: run.run_attempt
|
|
428
|
-
}
|
|
429
|
-
});
|
|
450
|
+
}));
|
|
451
|
+
for (const e of pageEvents) {
|
|
452
|
+
await storage.event(e);
|
|
430
453
|
}
|
|
431
454
|
const lastRun = runs.at(-1);
|
|
432
455
|
if (cutoff !== null && new Date(lastRun.created_at).getTime() < cutoff && new Date(lastRun.updated_at).getTime() < cutoff) {
|
|
433
|
-
|
|
434
|
-
}
|
|
435
|
-
if (stop) {
|
|
436
|
-
break;
|
|
456
|
+
return { done: true };
|
|
437
457
|
}
|
|
458
|
+
nextUrl = nextLink;
|
|
438
459
|
}
|
|
439
|
-
|
|
460
|
+
return { done: true };
|
|
440
461
|
}
|
|
441
|
-
async syncPullRequests(storage, signal) {
|
|
462
|
+
async syncPullRequests(storage, initialPageUrl, signal) {
|
|
442
463
|
const { owner, repo } = this.settings;
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
signal
|
|
447
|
-
)) {
|
|
448
|
-
allPRs.push(pr);
|
|
464
|
+
if (initialPageUrl === void 0) {
|
|
465
|
+
await storage.entities([], { types: ["pull_request"] });
|
|
466
|
+
await storage.edges([], { kinds: ["reviewed_by"] });
|
|
449
467
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
title: pr.title,
|
|
456
|
-
state: pr.state,
|
|
457
|
-
draft: pr.draft,
|
|
458
|
-
author: pr.user.login,
|
|
459
|
-
created_at: new Date(pr.created_at).getTime()
|
|
460
|
-
},
|
|
461
|
-
updated_at: new Date(pr.updated_at).getTime()
|
|
462
|
-
})),
|
|
463
|
-
{ types: ["pull_request"] }
|
|
464
|
-
);
|
|
465
|
-
const reviewEdges = [];
|
|
466
|
-
for (const pr of allPRs) {
|
|
467
|
-
signal?.throwIfAborted();
|
|
468
|
+
let nextUrl = initialPageUrl ?? `https://api.github.com/repos/${owner}/${repo}/pulls?state=all&per_page=100`;
|
|
469
|
+
while (nextUrl) {
|
|
470
|
+
if (signal?.aborted) {
|
|
471
|
+
return { done: false, pageUrl: nextUrl };
|
|
472
|
+
}
|
|
468
473
|
const res = await this.get(
|
|
469
|
-
|
|
474
|
+
nextUrl,
|
|
470
475
|
signal
|
|
471
476
|
);
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
477
|
+
const body = res.body;
|
|
478
|
+
const nextLink = parseLinkHeader(res.headers.get("link"))["next"] ?? null;
|
|
479
|
+
if (body.length === 0) {
|
|
480
|
+
return { done: true };
|
|
481
|
+
}
|
|
482
|
+
for (const pr of body) {
|
|
483
|
+
await storage.entity({
|
|
484
|
+
type: "pull_request",
|
|
485
|
+
id: String(pr.number),
|
|
486
|
+
attributes: {
|
|
487
|
+
title: pr.title,
|
|
488
|
+
state: pr.state,
|
|
489
|
+
draft: pr.draft,
|
|
490
|
+
author: pr.user.login,
|
|
491
|
+
created_at: new Date(pr.created_at).getTime()
|
|
492
|
+
},
|
|
493
|
+
updated_at: new Date(pr.updated_at).getTime()
|
|
484
494
|
});
|
|
485
495
|
}
|
|
496
|
+
for (const pr of body) {
|
|
497
|
+
if (signal?.aborted) {
|
|
498
|
+
return { done: false, pageUrl: nextUrl };
|
|
499
|
+
}
|
|
500
|
+
const res2 = await this.get(
|
|
501
|
+
`https://api.github.com/repos/${owner}/${repo}/pulls/${pr.number}/reviews`,
|
|
502
|
+
signal
|
|
503
|
+
);
|
|
504
|
+
for (const review of res2.body) {
|
|
505
|
+
if (!review.user) {
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
await storage.edge({
|
|
509
|
+
from_type: "pull_request",
|
|
510
|
+
from_id: String(pr.number),
|
|
511
|
+
kind: "reviewed_by",
|
|
512
|
+
to_type: "user",
|
|
513
|
+
to_id: review.user.login,
|
|
514
|
+
attributes: { state: review.state },
|
|
515
|
+
updated_at: new Date(review.submitted_at).getTime()
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
nextUrl = nextLink;
|
|
486
520
|
}
|
|
487
|
-
|
|
521
|
+
return { done: true };
|
|
488
522
|
}
|
|
489
|
-
async syncIssues(storage, options, signal) {
|
|
523
|
+
async syncIssues(storage, options, initialPageUrl, signal) {
|
|
490
524
|
const { owner, repo } = this.settings;
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
url.searchParams.set("per_page", "100");
|
|
494
|
-
if (options.since) {
|
|
495
|
-
url.searchParams.set("since", options.since);
|
|
525
|
+
if (initialPageUrl === void 0) {
|
|
526
|
+
await storage.entities([], { types: ["issue"] });
|
|
496
527
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
528
|
+
let nextUrl;
|
|
529
|
+
if (initialPageUrl) {
|
|
530
|
+
nextUrl = initialPageUrl;
|
|
531
|
+
} else {
|
|
532
|
+
const url = new URL(
|
|
533
|
+
`https://api.github.com/repos/${owner}/${repo}/issues`
|
|
534
|
+
);
|
|
535
|
+
url.searchParams.set("state", "all");
|
|
536
|
+
url.searchParams.set("per_page", "100");
|
|
537
|
+
if (options.since) {
|
|
538
|
+
url.searchParams.set("since", options.since);
|
|
504
539
|
}
|
|
505
|
-
|
|
506
|
-
}
|
|
507
|
-
await storage.entities(
|
|
508
|
-
allIssues.map((issue) => ({
|
|
509
|
-
type: "issue",
|
|
510
|
-
id: String(issue.number),
|
|
511
|
-
attributes: {
|
|
512
|
-
number: issue.number,
|
|
513
|
-
title: issue.title,
|
|
514
|
-
state: issue.state,
|
|
515
|
-
labels: issue.labels.map((l) => l.name),
|
|
516
|
-
assignees: issue.assignees.map((a) => a.login),
|
|
517
|
-
author: issue.user.login,
|
|
518
|
-
created_at: new Date(issue.created_at).getTime(),
|
|
519
|
-
updated_at: new Date(issue.updated_at).getTime(),
|
|
520
|
-
closed_at: issue.closed_at ? new Date(issue.closed_at).getTime() : null
|
|
521
|
-
},
|
|
522
|
-
updated_at: new Date(issue.updated_at).getTime()
|
|
523
|
-
})),
|
|
524
|
-
{ types: ["issue"] }
|
|
525
|
-
);
|
|
526
|
-
}
|
|
527
|
-
async syncDeployments(storage, signal) {
|
|
528
|
-
const { owner, repo } = this.settings;
|
|
529
|
-
const allDeployments = [];
|
|
530
|
-
for await (const d of this.paginate(
|
|
531
|
-
`https://api.github.com/repos/${owner}/${repo}/deployments?per_page=100`,
|
|
532
|
-
signal
|
|
533
|
-
)) {
|
|
534
|
-
allDeployments.push(d);
|
|
540
|
+
nextUrl = url.toString();
|
|
535
541
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
542
|
+
while (nextUrl) {
|
|
543
|
+
if (signal?.aborted) {
|
|
544
|
+
return { done: false, pageUrl: nextUrl };
|
|
545
|
+
}
|
|
539
546
|
const res = await this.get(
|
|
540
|
-
|
|
547
|
+
nextUrl,
|
|
541
548
|
signal
|
|
542
549
|
);
|
|
543
|
-
const
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
550
|
+
const body = res.body;
|
|
551
|
+
const nextLink = parseLinkHeader(res.headers.get("link"))["next"] ?? null;
|
|
552
|
+
if (body.length === 0) {
|
|
553
|
+
return { done: true };
|
|
554
|
+
}
|
|
555
|
+
for (const issue of body) {
|
|
556
|
+
if (issue.pull_request !== void 0) {
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
await storage.entity({
|
|
560
|
+
type: "issue",
|
|
561
|
+
id: String(issue.number),
|
|
562
|
+
attributes: {
|
|
563
|
+
number: issue.number,
|
|
564
|
+
title: issue.title,
|
|
565
|
+
state: issue.state,
|
|
566
|
+
labels: issue.labels.map((l) => l.name),
|
|
567
|
+
assignees: issue.assignees.map((a) => a.login),
|
|
568
|
+
author: issue.user.login,
|
|
569
|
+
created_at: new Date(issue.created_at).getTime(),
|
|
570
|
+
updated_at: new Date(issue.updated_at).getTime(),
|
|
571
|
+
closed_at: issue.closed_at ? new Date(issue.closed_at).getTime() : null
|
|
572
|
+
},
|
|
573
|
+
updated_at: new Date(issue.updated_at).getTime()
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
nextUrl = nextLink;
|
|
558
577
|
}
|
|
559
|
-
|
|
578
|
+
return { done: true };
|
|
560
579
|
}
|
|
561
|
-
async
|
|
580
|
+
async syncDeployments(storage, initialPageUrl, signal) {
|
|
562
581
|
const { owner, repo } = this.settings;
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
`https://api.github.com/repos/${owner}/${repo}/releases?per_page=100`,
|
|
566
|
-
signal
|
|
567
|
-
)) {
|
|
568
|
-
allReleases.push(r);
|
|
582
|
+
if (initialPageUrl === void 0) {
|
|
583
|
+
await storage.entities([], { types: ["deployment"] });
|
|
569
584
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
585
|
+
let nextUrl = initialPageUrl ?? `https://api.github.com/repos/${owner}/${repo}/deployments?per_page=100`;
|
|
586
|
+
while (nextUrl) {
|
|
587
|
+
if (signal?.aborted) {
|
|
588
|
+
return { done: false, pageUrl: nextUrl };
|
|
589
|
+
}
|
|
590
|
+
const res = await this.get(nextUrl, signal);
|
|
591
|
+
const body = res.body;
|
|
592
|
+
const nextLink = parseLinkHeader(res.headers.get("link"))["next"] ?? null;
|
|
593
|
+
if (body.length === 0) {
|
|
594
|
+
return { done: true };
|
|
595
|
+
}
|
|
596
|
+
for (const deployment of body) {
|
|
597
|
+
if (signal?.aborted) {
|
|
598
|
+
return { done: false, pageUrl: nextUrl };
|
|
599
|
+
}
|
|
600
|
+
const res2 = await this.get(
|
|
601
|
+
`https://api.github.com/repos/${owner}/${repo}/deployments/${deployment.id}/statuses?per_page=1`,
|
|
602
|
+
signal
|
|
603
|
+
);
|
|
604
|
+
const createdMs = new Date(deployment.created_at).getTime();
|
|
605
|
+
const statusUpdatedMs = res2.body[0]?.updated_at ? new Date(res2.body[0].updated_at).getTime() : null;
|
|
606
|
+
await storage.entity({
|
|
607
|
+
type: "deployment",
|
|
608
|
+
id: String(deployment.id),
|
|
609
|
+
attributes: {
|
|
610
|
+
environment: deployment.environment,
|
|
611
|
+
ref: deployment.ref,
|
|
612
|
+
sha: deployment.sha,
|
|
613
|
+
creator: deployment.creator?.login ?? "",
|
|
614
|
+
created_at: createdMs,
|
|
615
|
+
latest_status: res2.body[0]?.state ?? "unknown"
|
|
616
|
+
},
|
|
617
|
+
updated_at: Math.max(createdMs, statusUpdatedMs ?? 0)
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
nextUrl = nextLink;
|
|
621
|
+
}
|
|
622
|
+
return { done: true };
|
|
623
|
+
}
|
|
624
|
+
async syncReleases(storage, initialPageUrl, signal) {
|
|
625
|
+
const { owner, repo } = this.settings;
|
|
626
|
+
if (initialPageUrl === void 0) {
|
|
627
|
+
await storage.entities([], { types: ["release"] });
|
|
628
|
+
}
|
|
629
|
+
let nextUrl = initialPageUrl ?? `https://api.github.com/repos/${owner}/${repo}/releases?per_page=100`;
|
|
630
|
+
while (nextUrl) {
|
|
631
|
+
if (signal?.aborted) {
|
|
632
|
+
return { done: false, pageUrl: nextUrl };
|
|
633
|
+
}
|
|
634
|
+
const res = await this.get(nextUrl, signal);
|
|
635
|
+
const body = res.body;
|
|
636
|
+
const nextLink = parseLinkHeader(res.headers.get("link"))["next"] ?? null;
|
|
637
|
+
if (body.length === 0) {
|
|
638
|
+
return { done: true };
|
|
639
|
+
}
|
|
640
|
+
for (const release of body) {
|
|
641
|
+
await storage.entity({
|
|
642
|
+
type: "release",
|
|
643
|
+
id: String(release.id),
|
|
644
|
+
attributes: {
|
|
645
|
+
tag_name: release.tag_name,
|
|
646
|
+
name: release.name ?? "",
|
|
647
|
+
draft: release.draft,
|
|
648
|
+
prerelease: release.prerelease,
|
|
649
|
+
created_at: new Date(release.created_at).getTime(),
|
|
650
|
+
published_at: release.published_at ? new Date(release.published_at).getTime() : null,
|
|
651
|
+
author: release.author.login
|
|
652
|
+
},
|
|
653
|
+
updated_at: new Date(
|
|
654
|
+
release.published_at ?? release.created_at
|
|
655
|
+
).getTime()
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
nextUrl = nextLink;
|
|
659
|
+
}
|
|
660
|
+
return { done: true };
|
|
589
661
|
}
|
|
590
662
|
async syncContributors(storage, signal) {
|
|
591
663
|
const { owner, repo } = this.settings;
|
|
@@ -611,7 +683,7 @@ var GitHubActionsConnector = class _GitHubActionsConnector extends BaseConnector
|
|
|
611
683
|
"[github-actions] Stats endpoint never became ready \u2014 skipping contributor sync and keeping previous data."
|
|
612
684
|
);
|
|
613
685
|
}
|
|
614
|
-
return;
|
|
686
|
+
return { done: true };
|
|
615
687
|
}
|
|
616
688
|
await storage.entities(
|
|
617
689
|
contributors.map((c) => {
|
|
@@ -632,37 +704,66 @@ var GitHubActionsConnector = class _GitHubActionsConnector extends BaseConnector
|
|
|
632
704
|
}),
|
|
633
705
|
{ types: ["contributor"] }
|
|
634
706
|
);
|
|
707
|
+
return { done: true };
|
|
635
708
|
}
|
|
636
|
-
|
|
709
|
+
sanitizeIncomingCursor(cursor) {
|
|
710
|
+
if (!isGitHubSyncCursor(cursor)) {
|
|
711
|
+
return void 0;
|
|
712
|
+
}
|
|
713
|
+
if (cursor.pageUrl === void 0) {
|
|
714
|
+
return cursor;
|
|
715
|
+
}
|
|
637
716
|
const { owner, repo } = this.settings;
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
717
|
+
const expectedPrefix = `/repos/${owner}/${repo}/`;
|
|
718
|
+
try {
|
|
719
|
+
const u = new URL(cursor.pageUrl);
|
|
720
|
+
if (u.protocol !== "https:" || u.host !== "api.github.com" || !u.pathname.startsWith(expectedPrefix)) {
|
|
721
|
+
return { phase: cursor.phase };
|
|
722
|
+
}
|
|
723
|
+
return cursor;
|
|
724
|
+
} catch {
|
|
725
|
+
return { phase: cursor.phase };
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
async runPhase(phase, storage, options, initialPageUrl, signal) {
|
|
729
|
+
switch (phase) {
|
|
730
|
+
case "repo_stats":
|
|
731
|
+
return this.syncRepoStats(storage, initialPageUrl, signal);
|
|
732
|
+
case "workflow_runs":
|
|
733
|
+
return options.mode === "latest" ? this.syncWorkflowRunsLatest(storage, signal) : this.syncWorkflowRunsFull(storage, options, initialPageUrl, signal);
|
|
734
|
+
case "pull_requests":
|
|
735
|
+
return this.syncPullRequests(storage, initialPageUrl, signal);
|
|
736
|
+
case "issues":
|
|
737
|
+
return this.syncIssues(storage, options, initialPageUrl, signal);
|
|
738
|
+
case "deployments":
|
|
739
|
+
return this.syncDeployments(storage, initialPageUrl, signal);
|
|
740
|
+
case "releases":
|
|
741
|
+
return this.syncReleases(storage, initialPageUrl, signal);
|
|
742
|
+
case "contributors":
|
|
743
|
+
return this.syncContributors(storage, signal);
|
|
744
|
+
}
|
|
657
745
|
}
|
|
658
746
|
async sync(options, storage, signal) {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
747
|
+
const incoming = this.sanitizeIncomingCursor(options.cursor);
|
|
748
|
+
const startIdx = incoming ? PHASE_ORDER.indexOf(incoming.phase) : 0;
|
|
749
|
+
for (let i = startIdx; i < PHASE_ORDER.length; i++) {
|
|
750
|
+
const phase = PHASE_ORDER[i];
|
|
751
|
+
const initialPageUrl = i === startIdx ? incoming?.pageUrl : void 0;
|
|
752
|
+
const result = await this.runPhase(
|
|
753
|
+
phase,
|
|
754
|
+
storage,
|
|
755
|
+
options,
|
|
756
|
+
initialPageUrl,
|
|
757
|
+
signal
|
|
758
|
+
);
|
|
759
|
+
if (!result.done) {
|
|
760
|
+
return {
|
|
761
|
+
done: false,
|
|
762
|
+
cursor: { phase, pageUrl: result.pageUrl }
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
return { done: true };
|
|
666
767
|
}
|
|
667
768
|
};
|
|
668
769
|
export {
|
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/pagination.ts","../src/github-actions.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","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, 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;\n\nexport interface RequestOptions {\n fetch?: FetchLike;\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 (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 const githubRateLimit: RateLimitPolicy = {\n parse(h) {\n const remainingRaw = h.get('x-ratelimit-remaining');\n const resetRaw = h.get('x-ratelimit-reset');\n if (remainingRaw === null || resetRaw === null) {\n return null;\n }\n const remaining = Number(remainingRaw);\n const reset = Number(resetRaw);\n if (!Number.isFinite(remaining) || !Number.isFinite(reset) || reset < 0) {\n return null;\n }\n return { remaining, resetAt: new Date(reset * 1000) };\n },\n};\n\nexport const sentryRateLimit: RateLimitPolicy = {\n parse(h) {\n const concurrent = h.get('x-sentry-rate-limit-remaining');\n const reset = h.get('x-sentry-rate-limit-reset');\n if (concurrent === null || reset === null) {\n return null;\n }\n const remaining = Number(concurrent);\n const resetSec = Number(reset);\n if (\n !Number.isFinite(remaining) ||\n !Number.isFinite(resetSec) ||\n resetSec < 0\n ) {\n return null;\n }\n return { remaining, resetAt: new Date(resetSec * 1000) };\n },\n};\n\nexport const linearRateLimit: RateLimitPolicy = {\n parse(h) {\n const remainingRaw = h.get('x-ratelimit-requests-remaining');\n const resetRaw = h.get('x-ratelimit-requests-reset');\n if (remainingRaw === null) {\n return null;\n }\n const remaining = Number(remainingRaw);\n if (!Number.isFinite(remaining)) {\n return null;\n }\n let resetAt: Date;\n if (resetRaw !== null) {\n const reset = Number(resetRaw);\n if (!Number.isFinite(reset) || reset < 0) {\n return null;\n }\n resetAt = new Date(reset);\n } else {\n resetAt = new Date(Date.now() + 60_000);\n }\n return { remaining, resetAt };\n },\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): AsyncIterable<T> {\n let next: string | null = initial.url;\n while (next) {\n const res: Awaited<ReturnType<typeof request>> = await request({\n ...initial,\n url: next,\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): AsyncIterable<T> {\n let req: HttpRequest = initial;\n while (true) {\n const res = await request(req);\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): AsyncIterable<T> {\n let page = 1;\n while (true) {\n const req = page === 1 ? initial : buildPage(initial, page);\n const res = await request(req);\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","import {\n type HttpRequest,\n type HttpResponse,\n githubRateLimit,\n paginateLink,\n request,\n} from '@rawdash/connector-shared';\nimport {\n BaseConnector,\n type CredentialsSchema,\n type StorageHandle,\n type SyncOptions,\n defineConfigFields,\n} from '@rawdash/core';\nimport { z } from 'zod';\n\nexport const configFields = defineConfigFields(\n z.object({\n owner: z.string().min(1).meta({\n label: 'Repository owner',\n description: 'GitHub username or organization name.',\n placeholder: 'rawdash',\n }),\n repo: z.string().min(1).meta({\n label: 'Repository',\n description: 'Repository name.',\n placeholder: 'rawdash',\n }),\n token: z.object({ $secret: z.string() }).optional().meta({\n label: 'Personal access token',\n description: 'GitHub PAT with `repo` scope.',\n secret: true,\n }),\n }),\n);\n\nexport interface GitHubActionsSettings {\n owner: string;\n repo: string;\n}\n\ninterface GitHubRunsResponse {\n workflow_runs: Array<{\n id: number;\n name: string;\n conclusion: string | null;\n status: string;\n head_branch: string | null;\n actor: { login: string } | null;\n created_at: string;\n updated_at: string;\n run_attempt: number;\n }>;\n}\n\ninterface GitHubPR {\n number: number;\n title: string;\n state: string;\n draft: boolean;\n user: { login: string };\n created_at: string;\n updated_at: string;\n}\n\ninterface GitHubReview {\n user: { login: string } | null;\n state: string;\n submitted_at: string;\n}\n\ninterface GitHubIssue {\n number: number;\n title: string;\n state: string;\n labels: Array<{ name: string }>;\n assignees: Array<{ login: string }>;\n user: { login: string };\n created_at: string;\n updated_at: string;\n closed_at: string | null;\n pull_request?: unknown;\n}\n\ninterface GitHubDeployment {\n id: number;\n environment: string;\n ref: string;\n sha: string;\n creator: { login: string } | null;\n created_at: string;\n}\n\ninterface GitHubDeploymentStatus {\n state: string;\n updated_at: string;\n}\n\ninterface GitHubRelease {\n id: number;\n tag_name: string;\n name: string | null;\n draft: boolean;\n prerelease: boolean;\n created_at: string;\n published_at: string | null;\n author: { login: string };\n}\n\ninterface GitHubContributorStats {\n total: number;\n weeks: Array<{ w: number; a: number; d: number; c: number }>;\n author: { login: string };\n}\n\ninterface GitHubRepo {\n stargazers_count: number;\n forks_count: number;\n subscribers_count: number;\n}\n\nconst githubCredentials = {\n token: {\n description: 'GitHub personal access token',\n auth: 'optional' as const,\n },\n} satisfies CredentialsSchema;\n\ntype GitHubCredentials = typeof githubCredentials;\n\nexport class GitHubActionsConnector extends BaseConnector<\n GitHubActionsSettings,\n GitHubCredentials\n> {\n static readonly id = 'github-actions';\n\n static create(input: unknown): GitHubActionsConnector {\n const parsed = configFields.parse(input);\n return new GitHubActionsConnector(\n { owner: parsed.owner, repo: parsed.repo },\n { token: parsed.token },\n );\n }\n\n readonly id = 'github-actions';\n\n override readonly credentials = githubCredentials;\n\n private buildHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n Accept: 'application/vnd.github+json',\n 'X-GitHub-Api-Version': '2022-11-28',\n 'User-Agent': 'rawdash/connector-github (+https://rawdash.dev)',\n };\n if (this.creds.token) {\n headers['Authorization'] = `Bearer ${this.creds.token}`;\n }\n return headers;\n }\n\n private get<T>(url: string, signal?: AbortSignal): Promise<HttpResponse<T>> {\n const req: HttpRequest = {\n url,\n headers: this.buildHeaders(),\n signal,\n rateLimit: githubRateLimit,\n };\n return request<T>(req);\n }\n\n private async *paginate<T>(\n url: string,\n signal?: AbortSignal,\n ): AsyncIterable<T> {\n yield* paginateLink<T>(\n {\n url,\n headers: this.buildHeaders(),\n signal,\n rateLimit: githubRateLimit,\n },\n (body) => body as T[],\n );\n }\n\n private async syncWorkflowRuns(\n storage: StorageHandle,\n options: SyncOptions,\n signal?: AbortSignal,\n ): Promise<void> {\n const { owner, repo } = this.settings;\n\n if (options.mode === 'latest') {\n const res = await this.get<GitHubRunsResponse>(\n `https://api.github.com/repos/${owner}/${repo}/actions/runs?per_page=1`,\n signal,\n );\n const run = res.body.workflow_runs[0];\n if (run) {\n await storage.event({\n name: 'workflow_run',\n start_ts: new Date(run.created_at).getTime(),\n end_ts: new Date(run.updated_at).getTime(),\n attributes: {\n id: run.id,\n workflow_name: run.name,\n conclusion: run.conclusion ?? 'unknown',\n status: run.status,\n branch: run.head_branch ?? '',\n actor: run.actor?.login ?? '',\n run_attempt: run.run_attempt,\n },\n });\n }\n return;\n }\n\n const cutoff = options.since ? new Date(options.since).getTime() : null;\n const allEvents: Parameters<StorageHandle['events']>[0] = [];\n\n const iter = paginateLink<GitHubRunsResponse>(\n {\n url: `https://api.github.com/repos/${owner}/${repo}/actions/runs?per_page=100`,\n headers: this.buildHeaders(),\n signal,\n rateLimit: githubRateLimit,\n },\n (body) => [body as GitHubRunsResponse],\n );\n\n let stop = false;\n for await (const page of iter) {\n signal?.throwIfAborted();\n const runs = page.workflow_runs;\n if (runs.length === 0) {\n break;\n }\n\n for (const run of runs) {\n const createdMs = new Date(run.created_at).getTime();\n const updatedMs = new Date(run.updated_at).getTime();\n if (cutoff !== null && createdMs < cutoff && updatedMs < cutoff) {\n continue;\n }\n allEvents.push({\n name: 'workflow_run',\n start_ts: createdMs,\n end_ts: updatedMs,\n attributes: {\n id: run.id,\n workflow_name: run.name,\n conclusion: run.conclusion ?? 'unknown',\n status: run.status,\n branch: run.head_branch ?? '',\n actor: run.actor?.login ?? '',\n run_attempt: run.run_attempt,\n },\n });\n }\n\n const lastRun = runs.at(-1)!;\n if (\n cutoff !== null &&\n new Date(lastRun.created_at).getTime() < cutoff &&\n new Date(lastRun.updated_at).getTime() < cutoff\n ) {\n stop = true;\n }\n if (stop) {\n break;\n }\n }\n\n await storage.events(allEvents, { names: ['workflow_run'] });\n }\n\n private async syncPullRequests(\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<void> {\n const { owner, repo } = this.settings;\n const allPRs: GitHubPR[] = [];\n\n for await (const pr of this.paginate<GitHubPR>(\n `https://api.github.com/repos/${owner}/${repo}/pulls?state=all&per_page=100`,\n signal,\n )) {\n allPRs.push(pr);\n }\n\n await storage.entities(\n allPRs.map((pr) => ({\n type: 'pull_request',\n id: String(pr.number),\n attributes: {\n title: pr.title,\n state: pr.state,\n draft: pr.draft,\n author: pr.user.login,\n created_at: new Date(pr.created_at).getTime(),\n },\n updated_at: new Date(pr.updated_at).getTime(),\n })),\n { types: ['pull_request'] },\n );\n\n const reviewEdges: Parameters<StorageHandle['edges']>[0] = [];\n for (const pr of allPRs) {\n signal?.throwIfAborted();\n const res = await this.get<GitHubReview[]>(\n `https://api.github.com/repos/${owner}/${repo}/pulls/${pr.number}/reviews`,\n signal,\n );\n for (const review of res.body) {\n if (!review.user) {\n continue;\n }\n reviewEdges.push({\n from_type: 'pull_request',\n from_id: String(pr.number),\n kind: 'reviewed_by',\n to_type: 'user',\n to_id: review.user.login,\n attributes: { state: review.state },\n updated_at: new Date(review.submitted_at).getTime(),\n });\n }\n }\n\n await storage.edges(reviewEdges, { kinds: ['reviewed_by'] });\n }\n\n private async syncIssues(\n storage: StorageHandle,\n options: SyncOptions,\n signal?: AbortSignal,\n ): Promise<void> {\n const { owner, repo } = this.settings;\n const url = new URL(`https://api.github.com/repos/${owner}/${repo}/issues`);\n url.searchParams.set('state', 'all');\n url.searchParams.set('per_page', '100');\n if (options.since) {\n url.searchParams.set('since', options.since);\n }\n\n const allIssues: GitHubIssue[] = [];\n for await (const issue of this.paginate<GitHubIssue>(\n url.toString(),\n signal,\n )) {\n if (issue.pull_request !== undefined) {\n continue;\n }\n allIssues.push(issue);\n }\n\n await storage.entities(\n allIssues.map((issue) => ({\n type: 'issue',\n id: String(issue.number),\n attributes: {\n number: issue.number,\n title: issue.title,\n state: issue.state,\n labels: issue.labels.map((l) => l.name),\n assignees: issue.assignees.map((a) => a.login),\n author: issue.user.login,\n created_at: new Date(issue.created_at).getTime(),\n updated_at: new Date(issue.updated_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 { types: ['issue'] },\n );\n }\n\n private async syncDeployments(\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<void> {\n const { owner, repo } = this.settings;\n const allDeployments: GitHubDeployment[] = [];\n for await (const d of this.paginate<GitHubDeployment>(\n `https://api.github.com/repos/${owner}/${repo}/deployments?per_page=100`,\n signal,\n )) {\n allDeployments.push(d);\n }\n\n const entities: Parameters<StorageHandle['entities']>[0] = [];\n for (const deployment of allDeployments) {\n signal?.throwIfAborted();\n const res = await this.get<GitHubDeploymentStatus[]>(\n `https://api.github.com/repos/${owner}/${repo}/deployments/${deployment.id}/statuses?per_page=1`,\n signal,\n );\n const createdMs = new Date(deployment.created_at).getTime();\n const statusUpdatedMs = res.body[0]?.updated_at\n ? new Date(res.body[0].updated_at).getTime()\n : null;\n entities.push({\n type: 'deployment',\n id: String(deployment.id),\n attributes: {\n environment: deployment.environment,\n ref: deployment.ref,\n sha: deployment.sha,\n creator: deployment.creator?.login ?? '',\n created_at: createdMs,\n latest_status: res.body[0]?.state ?? 'unknown',\n },\n updated_at: Math.max(createdMs, statusUpdatedMs ?? 0),\n });\n }\n\n await storage.entities(entities, { types: ['deployment'] });\n }\n\n private async syncReleases(\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<void> {\n const { owner, repo } = this.settings;\n const allReleases: GitHubRelease[] = [];\n for await (const r of this.paginate<GitHubRelease>(\n `https://api.github.com/repos/${owner}/${repo}/releases?per_page=100`,\n signal,\n )) {\n allReleases.push(r);\n }\n\n await storage.entities(\n allReleases.map((release) => ({\n type: 'release',\n id: String(release.id),\n attributes: {\n tag_name: release.tag_name,\n name: release.name ?? '',\n draft: release.draft,\n prerelease: release.prerelease,\n created_at: new Date(release.created_at).getTime(),\n published_at: release.published_at\n ? new Date(release.published_at).getTime()\n : null,\n author: release.author.login,\n },\n updated_at: new Date(\n release.published_at ?? release.created_at,\n ).getTime(),\n })),\n { types: ['release'] },\n );\n }\n\n private async syncContributors(\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<void> {\n const { owner, repo } = this.settings;\n\n const contributors = await this.withRetry<GitHubContributorStats[]>(\n async (sig) => {\n const res = await this.get<GitHubContributorStats[] | null>(\n `https://api.github.com/repos/${owner}/${repo}/stats/contributors`,\n sig,\n );\n if (res.status === 202) {\n return { status: 'retry' };\n }\n return {\n status: 'done',\n value: (res.body ?? []) as GitHubContributorStats[],\n };\n },\n { maxAttempts: 15, initialDelayMs: 1000, maxDelayMs: 10000, signal },\n );\n\n if (!contributors || contributors.length === 0) {\n if (!contributors) {\n console.warn(\n '[github-actions] Stats endpoint never became ready — skipping contributor sync and keeping previous data.',\n );\n }\n return;\n }\n\n await storage.entities(\n contributors.map((c) => {\n const additions = c.weeks.reduce((sum, w) => sum + w.a, 0);\n const deletions = c.weeks.reduce((sum, w) => sum + w.d, 0);\n const latestWeek = [...c.weeks].reverse().find((w) => w.c > 0);\n return {\n type: 'contributor',\n id: c.author.login,\n attributes: {\n commits: c.total,\n additions,\n deletions,\n latest_commit_at: latestWeek ? latestWeek.w * 1000 : null,\n },\n updated_at: latestWeek ? latestWeek.w * 1000 : 0,\n };\n }),\n { types: ['contributor'] },\n );\n }\n\n private async syncRepoStats(\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<void> {\n const { owner, repo } = this.settings;\n const res = await this.get<GitHubRepo>(\n `https://api.github.com/repos/${owner}/${repo}`,\n signal,\n );\n await storage.entities(\n [\n {\n type: 'repo',\n id: `${owner}/${repo}`,\n attributes: {\n stars: res.body.stargazers_count,\n forks: res.body.forks_count,\n watchers: res.body.subscribers_count,\n },\n updated_at: Date.now(),\n },\n ],\n { types: ['repo'] },\n );\n }\n\n async sync(\n options: SyncOptions,\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<void> {\n await this.syncRepoStats(storage, signal);\n await this.syncWorkflowRuns(storage, options, signal);\n await this.syncPullRequests(storage, signal);\n await this.syncIssues(storage, options, signal);\n await this.syncDeployments(storage, signal);\n await this.syncReleases(storage, signal);\n await this.syncContributors(storage, signal);\n }\n}\n"],"mappings":";AASO,IAAe,kBAAf,cAAuC,MAAM;EAEzC;EAET,YAAY,SAAiB,UAAyB;AACpD,UAAM,OAAO;AACb,SAAK,OAAO,WAAW;AACvB,SAAK,WAAW;EAClB;AACF;AAEO,IAAM,iBAAN,cAA6B,gBAAgB;EACzC,OAAO;AAClB;AAEO,IAAM,iBAAN,cAA6B,gBAAgB;EACzC,OAAO;EACP;EAET,YAAY,SAAiB,UAAyB,YAAmB;AACvE,UAAM,SAAS,QAAQ;AACvB,SAAK,aAAa;EACpB;AACF;AAEO,IAAM,YAAN,cAAwB,gBAAgB;EACpC,OAAO;AAClB;AAEO,IAAM,mBAAN,cAA+B,gBAAgB;EAC3C,OAAO;AAClB;AAEO,IAAM,iBAAN,cAA6B,gBAAgB;EACzC,OAAO;AAClB;AAEO,SAAS,eAAe,QAA+B;AAC5D,MAAI,WAAW,KAAK;AAClB,WAAO;EACT;AACA,MAAI,WAAW,OAAO,WAAW,KAAK;AACpC,WAAO;EACT;AACA,MAAI,WAAW,KAAK;AAClB,WAAO;EACT;AACA,MAAI,UAAU,KAAK;AACjB,WAAO;EACT;AACA,MAAI,UAAU,KAAK;AACjB,WAAO;EACT;AACA,SAAO;AACT;AAEO,SAAS,eACd,SACA,UACA,YACiB;AACjB,QAAM,OAAO,eAAe,SAAS,MAAM;AAC3C,UAAQ,MAAM;IACZ,KAAK;AACH,aAAO,IAAI,eAAe,SAAS,UAAU,UAAU;IACzD,KAAK;AACH,aAAO,IAAI,UAAU,SAAS,QAAQ;IACxC,KAAK;AACH,aAAO,IAAI,eAAe,SAAS,QAAQ;IAC7C,KAAK;AACH,aAAO,IAAI,iBAAiB,SAAS,QAAQ;IAC/C,KAAK;AACH,aAAO,IAAI,eAAe,SAAS,QAAQ;EAC/C;AACF;AC1EO,IAAM,iBAAiB,CAAC,QAAuB,QAAyB;AAC7E,MAAI,eAAe,gBAAgB;AACjC,WAAO;EACT;AACA,MAAI,eAAe,gBAAgB;AACjC,WAAO;EACT;AACA,MAAI,WAAW,MAAM;AACnB,WAAO,eAAe,SAAS,EAAE,eAAe;EAClD;AACA,MAAI,WAAW,OAAO,WAAW,KAAK;AACpC,WAAO;EACT;AACA,MAAI,UAAU,KAAK;AACjB,WAAO;EACT;AACA,SAAO;AACT;AAWO,SAAS,gBACd,aACA,MAAY,oBAAI,KAAK,GACH;AAClB,MAAI,CAAC,aAAa;AAChB,WAAO;EACT;AACA,QAAM,UAAU,YAAY,KAAK;AACjC,MAAI,QAAQ,KAAK,OAAO,GAAG;AACzB,WAAO,IAAI,KAAK,IAAI,QAAQ,IAAI,OAAO,OAAO,IAAI,GAAI;EACxD;AACA,QAAM,SAAS,KAAK,MAAM,OAAO;AACjC,MAAI,OAAO,MAAM,MAAM,GAAG;AACxB,WAAO;EACT;AACA,SAAO,IAAI,KAAK,MAAM;AACxB;AAEO,SAAS,MAAM,IAAY,QAAqC;AACrE,MAAI,QAAQ,SAAS;AACnB,WAAO,QAAQ,OAAO,OAAO,UAAU,IAAI,MAAM,SAAS,CAAC;EAC7D;AACA,SAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,UAAM,UAAU,MAAM;AACpB,mBAAa,KAAK;AAClB,aAAO,OAAQ,UAAU,IAAI,MAAM,SAAS,CAAC;IAC/C;AACA,UAAM,QAAQ,WAAW,MAAM;AAC7B,cAAQ,oBAAoB,SAAS,OAAO;AAC5C,cAAQ;IACV,GAAG,EAAE;AACL,YAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EAC3D,CAAC;AACH;ACtEO,IAAM,sBAAsB;AAE5B,IAAM,qBAAqB,qBAAqB,mBAAmB;ACW1E,IAAM,qBAAqB;AAC3B,IAAM,uBAAuB;AAC7B,IAAM,2BAA2B;AACjC,IAAM,uBAAuB;AAM7B,SAAS,aACP,UACA,WACwB;AACxB,QAAM,SAAiC,CAAC;AACxC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAC7C,WAAO,EAAE,YAAY,CAAC,IAAI;EAC5B;AACA,MAAI,WAAW;AACb,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,SAAS,GAAG;AAC9C,aAAO,EAAE,YAAY,CAAC,IAAI;IAC5B;EACF;AACA,SAAO;AACT;AAEA,SAAS,kBACP,QACA,WAC6C;AAC7C,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,gBAAgB,MAAM;AAC1B,eAAW,MAAM,QAAQ,MAAM;EACjC;AACA,MAAI,QAAQ;AACV,QAAI,OAAO,SAAS;AAClB,iBAAW,MAAM,OAAO,MAAM;IAChC,OAAO;AACL,aAAO,iBAAiB,SAAS,eAAe,EAAE,MAAM,KAAK,CAAC;IAChE;EACF;AACA,QAAM,QAAQ,WAAW,MAAM;AAC7B,eAAW,MAAM,IAAI,MAAM,2BAA2B,SAAS,IAAI,CAAC;EACtE,GAAG,SAAS;AACZ,SAAO;IACL,QAAQ,WAAW;IACnB,QAAQ,MAAM;AACZ,mBAAa,KAAK;AAClB,UAAI,QAAQ;AACV,eAAO,oBAAoB,SAAS,aAAa;MACnD;IACF;EACF;AACF;AAEA,eAAe,SAAS,KAAe,WAAsC;AAC3E,MAAI,IAAI,WAAW,OAAO,IAAI,WAAW,KAAK;AAC5C,WAAO;EACT;AACA,QAAM,cAAc,IAAI,QAAQ,IAAI,cAAc,KAAK;AACvD,MAAI,aAAa,YAAY,SAAS,kBAAkB,GAAG;AACzD,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI,KAAK,WAAW,GAAG;AACrB,aAAO;IACT;AACA,WAAO,KAAK,MAAM,IAAI;EACxB;AACA,SAAO,IAAI,KAAK;AAClB;AAEA,eAAsB,QACpB,KACA,UAA0B,CAAC,GACD;AAC1B,QAAM,YAAuB,QAAQ,SAAU,WAAW;AAC1D,QAAM,QAAQ,IAAI,SAAS,CAAC;AAC5B,QAAM,cAAc,MAAM,eAAe;AACzC,QAAM,iBAAiB,MAAM,kBAAkB;AAC/C,QAAM,aAAa,MAAM,cAAc;AACvC,QAAM,UAAU,MAAM,WAAW;AACjC,QAAM,YAAY,IAAI,aAAa;AACnC,QAAM,YAAY,IAAI,aAAa;AAEnC,QAAM,UAAU;IACd;MACE,cAAc;MACd,QAAQ;IACV;IACA,IAAI;EACN;AAEA,MAAI;AAEJ,WAAS,UAAU,GAAG,UAAU,aAAa,WAAW;AACtD,QAAI,QAAQ,eAAe;AAE3B,UAAM,EAAE,QAAQ,OAAO,IAAI,kBAAkB,IAAI,QAAQ,SAAS;AAClE,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,UAAU,IAAI,KAAK;QAC7B,QAAQ,IAAI,UAAU;QACtB;QACA,MAAM,IAAI;QACV;MACF,CAAC;IACH,SAASA,MAAK;AACZ,aAAO;AACP,UAAI,IAAI,QAAQ,SAAS;AACvB,cAAM,IAAI,OAAO,UAAUA;MAC7B;AACA,YAAM,QAAQA,gBAAe,QAAQA,OAAM,IAAI,MAAM,OAAOA,IAAG,CAAC;AAChE,gBAAU;AACV,UAAI,UAAU,cAAc,KAAK,QAAQ,MAAM,KAAK,GAAG;AACrD,cAAM,QAAQ,aAAa,SAAS,gBAAgB,UAAU;AAC9D,cAAM,MAAM,OAAO,IAAI,MAAM;AAC7B;MACF;AACA,YAAM,IAAI,eAAe,MAAM,OAAO;IACxC;AACA,WAAO;AAEP,UAAM,OAAO,MAAM,SAAS,KAAK,SAAS;AAC1C,UAAM,eAAgC;MACpC,QAAQ,IAAI;MACZ,SAAS,IAAI;MACb;IACF;AACA,QAAI,IAAI,WAAW;AACjB,YAAM,QAAQ,IAAI,UAAU,MAAM,IAAI,OAAO;AAC7C,UAAI,OAAO;AACT,qBAAa,iBAAiB;MAChC;IACF;AAEA,QAAI,IAAI,IAAI;AACV,aAAO;IACT;AAEA,UAAM,aAAa,gBAAgB,IAAI,QAAQ,IAAI,aAAa,CAAC;AACjE,UAAM,UAAU,QAAQ,IAAI,MAAM,IAAI,IAAI,UAAU,QAAQ,IAAI,UAAU,KAAK,IAAI,IAAI,GAAG;AAC1F,UAAM,MAAM,eAAe,SAAS,cAAc,UAAU;AAE5D,QACE,UAAU,cAAc,KACxB,QAAQ,IAAI,QAAQ,GAAG,KACvB,EAAE,eAAe,cACjB,EAAE,eAAe,iBACjB;AACA,gBAAU;AACV,UAAI,QAAQ,aAAa,SAAS,gBAAgB,UAAU;AAC5D,UAAI,eAAe,kBAAkB,YAAY;AAC/C,cAAM,OAAO,WAAW,QAAQ,IAAI,KAAK,IAAI;AAC7C,YAAI,OAAO,GAAG;AACZ,kBAAQ,KAAK,IAAI,MAAM,UAAU;QACnC;MACF;AACA,YAAM,MAAM,OAAO,IAAI,MAAM;AAC7B;IACF;AAEA,UAAM;EACR;AAEA,QAAM,WAAW,IAAI,iBAAiB,0BAA0B;AAClE;AAEA,SAAS,aACP,SACA,gBACA,YACQ;AACR,QAAM,OAAO,iBAAiB,KAAK;AACnC,QAAM,SAAS,OAAO,OAAO,KAAK,OAAO;AACzC,SAAO,KAAK,IAAI,OAAO,QAAQ,UAAU;AAC3C;ACjLO,IAAM,kBAAmC;EAC9C,MAAM,GAAG;AACP,UAAM,eAAe,EAAE,IAAI,uBAAuB;AAClD,UAAM,WAAW,EAAE,IAAI,mBAAmB;AAC1C,QAAI,iBAAiB,QAAQ,aAAa,MAAM;AAC9C,aAAO;IACT;AACA,UAAM,YAAY,OAAO,YAAY;AACrC,UAAM,QAAQ,OAAO,QAAQ;AAC7B,QAAI,CAAC,OAAO,SAAS,SAAS,KAAK,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,GAAG;AACvE,aAAO;IACT;AACA,WAAO,EAAE,WAAW,SAAS,IAAI,KAAK,QAAQ,GAAI,EAAE;EACtD;AACF;ACpBO,SAAS,gBAAgB,QAA+C;AAC7E,MAAI,CAAC,QAAQ;AACX,WAAO,CAAC;EACV;AACA,QAAM,SAAiC,CAAC;AACxC,aAAW,QAAQ,OAAO,MAAM,GAAG,GAAG;AACpC,UAAM,QAAQ,KAAK,MAAM,+BAA+B;AACxD,QAAI,OAAO;AACT,aAAO,MAAM,CAAC,CAAE,IAAI,MAAM,CAAC;IAC7B;EACF;AACA,SAAO;AACT;AAEA,gBAAuB,aACrB,SACA,OACkB;AAClB,MAAI,OAAsB,QAAQ;AAClC,SAAO,MAAM;AACX,UAAM,MAA2C,MAAM,QAAQ;MAC7D,GAAG;MACH,KAAK;IACP,CAAC;AACD,eAAW,QAAQ,MAAM,IAAI,IAAI,GAAG;AAClC,YAAM;IACR;AACA,UAAM,QAAQ,gBAAgB,IAAI,QAAQ,IAAI,MAAM,CAAC;AACrD,WAAO,MAAM,MAAM,KAAK;EAC1B;AACF;;;AC1BA;AAAA,EACE;AAAA,EAIA;AAAA,OACK;AACP,SAAS,SAAS;AAEX,IAAM,eAAe;AAAA,EAC1B,EAAE,OAAO;AAAA,IACP,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,KAAK;AAAA,MAC5B,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,IACf,CAAC;AAAA,IACD,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,KAAK;AAAA,MAC3B,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,IACf,CAAC;AAAA,IACD,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,KAAK;AAAA,MACvD,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,IACV,CAAC;AAAA,EACH,CAAC;AACH;AAuFA,IAAM,oBAAoB;AAAA,EACxB,OAAO;AAAA,IACL,aAAa;AAAA,IACb,MAAM;AAAA,EACR;AACF;AAIO,IAAM,yBAAN,MAAM,gCAA+B,cAG1C;AAAA,EACA,OAAgB,KAAK;AAAA,EAErB,OAAO,OAAO,OAAwC;AACpD,UAAM,SAAS,aAAa,MAAM,KAAK;AACvC,WAAO,IAAI;AAAA,MACT,EAAE,OAAO,OAAO,OAAO,MAAM,OAAO,KAAK;AAAA,MACzC,EAAE,OAAO,OAAO,MAAM;AAAA,IACxB;AAAA,EACF;AAAA,EAES,KAAK;AAAA,EAEI,cAAc;AAAA,EAExB,eAAuC;AAC7C,UAAM,UAAkC;AAAA,MACtC,QAAQ;AAAA,MACR,wBAAwB;AAAA,MACxB,cAAc;AAAA,IAChB;AACA,QAAI,KAAK,MAAM,OAAO;AACpB,cAAQ,eAAe,IAAI,UAAU,KAAK,MAAM,KAAK;AAAA,IACvD;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,IAAO,KAAa,QAAgD;AAC1E,UAAM,MAAmB;AAAA,MACvB;AAAA,MACA,SAAS,KAAK,aAAa;AAAA,MAC3B;AAAA,MACA,WAAW;AAAA,IACb;AACA,WAAO,QAAW,GAAG;AAAA,EACvB;AAAA,EAEA,OAAe,SACb,KACA,QACkB;AAClB,WAAO;AAAA,MACL;AAAA,QACE;AAAA,QACA,SAAS,KAAK,aAAa;AAAA,QAC3B;AAAA,QACA,WAAW;AAAA,MACb;AAAA,MACA,CAAC,SAAS;AAAA,IACZ;AAAA,EACF;AAAA,EAEA,MAAc,iBACZ,SACA,SACA,QACe;AACf,UAAM,EAAE,OAAO,KAAK,IAAI,KAAK;AAE7B,QAAI,QAAQ,SAAS,UAAU;AAC7B,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB,gCAAgC,KAAK,IAAI,IAAI;AAAA,QAC7C;AAAA,MACF;AACA,YAAM,MAAM,IAAI,KAAK,cAAc,CAAC;AACpC,UAAI,KAAK;AACP,cAAM,QAAQ,MAAM;AAAA,UAClB,MAAM;AAAA,UACN,UAAU,IAAI,KAAK,IAAI,UAAU,EAAE,QAAQ;AAAA,UAC3C,QAAQ,IAAI,KAAK,IAAI,UAAU,EAAE,QAAQ;AAAA,UACzC,YAAY;AAAA,YACV,IAAI,IAAI;AAAA,YACR,eAAe,IAAI;AAAA,YACnB,YAAY,IAAI,cAAc;AAAA,YAC9B,QAAQ,IAAI;AAAA,YACZ,QAAQ,IAAI,eAAe;AAAA,YAC3B,OAAO,IAAI,OAAO,SAAS;AAAA,YAC3B,aAAa,IAAI;AAAA,UACnB;AAAA,QACF,CAAC;AAAA,MACH;AACA;AAAA,IACF;AAEA,UAAM,SAAS,QAAQ,QAAQ,IAAI,KAAK,QAAQ,KAAK,EAAE,QAAQ,IAAI;AACnE,UAAM,YAAoD,CAAC;AAE3D,UAAM,OAAO;AAAA,MACX;AAAA,QACE,KAAK,gCAAgC,KAAK,IAAI,IAAI;AAAA,QAClD,SAAS,KAAK,aAAa;AAAA,QAC3B;AAAA,QACA,WAAW;AAAA,MACb;AAAA,MACA,CAAC,SAAS,CAAC,IAA0B;AAAA,IACvC;AAEA,QAAI,OAAO;AACX,qBAAiB,QAAQ,MAAM;AAC7B,cAAQ,eAAe;AACvB,YAAM,OAAO,KAAK;AAClB,UAAI,KAAK,WAAW,GAAG;AACrB;AAAA,MACF;AAEA,iBAAW,OAAO,MAAM;AACtB,cAAM,YAAY,IAAI,KAAK,IAAI,UAAU,EAAE,QAAQ;AACnD,cAAM,YAAY,IAAI,KAAK,IAAI,UAAU,EAAE,QAAQ;AACnD,YAAI,WAAW,QAAQ,YAAY,UAAU,YAAY,QAAQ;AAC/D;AAAA,QACF;AACA,kBAAU,KAAK;AAAA,UACb,MAAM;AAAA,UACN,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,YAAY;AAAA,YACV,IAAI,IAAI;AAAA,YACR,eAAe,IAAI;AAAA,YACnB,YAAY,IAAI,cAAc;AAAA,YAC9B,QAAQ,IAAI;AAAA,YACZ,QAAQ,IAAI,eAAe;AAAA,YAC3B,OAAO,IAAI,OAAO,SAAS;AAAA,YAC3B,aAAa,IAAI;AAAA,UACnB;AAAA,QACF,CAAC;AAAA,MACH;AAEA,YAAM,UAAU,KAAK,GAAG,EAAE;AAC1B,UACE,WAAW,QACX,IAAI,KAAK,QAAQ,UAAU,EAAE,QAAQ,IAAI,UACzC,IAAI,KAAK,QAAQ,UAAU,EAAE,QAAQ,IAAI,QACzC;AACA,eAAO;AAAA,MACT;AACA,UAAI,MAAM;AACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,QAAQ,OAAO,WAAW,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC;AAAA,EAC7D;AAAA,EAEA,MAAc,iBACZ,SACA,QACe;AACf,UAAM,EAAE,OAAO,KAAK,IAAI,KAAK;AAC7B,UAAM,SAAqB,CAAC;AAE5B,qBAAiB,MAAM,KAAK;AAAA,MAC1B,gCAAgC,KAAK,IAAI,IAAI;AAAA,MAC7C;AAAA,IACF,GAAG;AACD,aAAO,KAAK,EAAE;AAAA,IAChB;AAEA,UAAM,QAAQ;AAAA,MACZ,OAAO,IAAI,CAAC,QAAQ;AAAA,QAClB,MAAM;AAAA,QACN,IAAI,OAAO,GAAG,MAAM;AAAA,QACpB,YAAY;AAAA,UACV,OAAO,GAAG;AAAA,UACV,OAAO,GAAG;AAAA,UACV,OAAO,GAAG;AAAA,UACV,QAAQ,GAAG,KAAK;AAAA,UAChB,YAAY,IAAI,KAAK,GAAG,UAAU,EAAE,QAAQ;AAAA,QAC9C;AAAA,QACA,YAAY,IAAI,KAAK,GAAG,UAAU,EAAE,QAAQ;AAAA,MAC9C,EAAE;AAAA,MACF,EAAE,OAAO,CAAC,cAAc,EAAE;AAAA,IAC5B;AAEA,UAAM,cAAqD,CAAC;AAC5D,eAAW,MAAM,QAAQ;AACvB,cAAQ,eAAe;AACvB,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB,gCAAgC,KAAK,IAAI,IAAI,UAAU,GAAG,MAAM;AAAA,QAChE;AAAA,MACF;AACA,iBAAW,UAAU,IAAI,MAAM;AAC7B,YAAI,CAAC,OAAO,MAAM;AAChB;AAAA,QACF;AACA,oBAAY,KAAK;AAAA,UACf,WAAW;AAAA,UACX,SAAS,OAAO,GAAG,MAAM;AAAA,UACzB,MAAM;AAAA,UACN,SAAS;AAAA,UACT,OAAO,OAAO,KAAK;AAAA,UACnB,YAAY,EAAE,OAAO,OAAO,MAAM;AAAA,UAClC,YAAY,IAAI,KAAK,OAAO,YAAY,EAAE,QAAQ;AAAA,QACpD,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;AAAA,EAC7D;AAAA,EAEA,MAAc,WACZ,SACA,SACA,QACe;AACf,UAAM,EAAE,OAAO,KAAK,IAAI,KAAK;AAC7B,UAAM,MAAM,IAAI,IAAI,gCAAgC,KAAK,IAAI,IAAI,SAAS;AAC1E,QAAI,aAAa,IAAI,SAAS,KAAK;AACnC,QAAI,aAAa,IAAI,YAAY,KAAK;AACtC,QAAI,QAAQ,OAAO;AACjB,UAAI,aAAa,IAAI,SAAS,QAAQ,KAAK;AAAA,IAC7C;AAEA,UAAM,YAA2B,CAAC;AAClC,qBAAiB,SAAS,KAAK;AAAA,MAC7B,IAAI,SAAS;AAAA,MACb;AAAA,IACF,GAAG;AACD,UAAI,MAAM,iBAAiB,QAAW;AACpC;AAAA,MACF;AACA,gBAAU,KAAK,KAAK;AAAA,IACtB;AAEA,UAAM,QAAQ;AAAA,MACZ,UAAU,IAAI,CAAC,WAAW;AAAA,QACxB,MAAM;AAAA,QACN,IAAI,OAAO,MAAM,MAAM;AAAA,QACvB,YAAY;AAAA,UACV,QAAQ,MAAM;AAAA,UACd,OAAO,MAAM;AAAA,UACb,OAAO,MAAM;AAAA,UACb,QAAQ,MAAM,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,UACtC,WAAW,MAAM,UAAU,IAAI,CAAC,MAAM,EAAE,KAAK;AAAA,UAC7C,QAAQ,MAAM,KAAK;AAAA,UACnB,YAAY,IAAI,KAAK,MAAM,UAAU,EAAE,QAAQ;AAAA,UAC/C,YAAY,IAAI,KAAK,MAAM,UAAU,EAAE,QAAQ;AAAA,UAC/C,WAAW,MAAM,YACb,IAAI,KAAK,MAAM,SAAS,EAAE,QAAQ,IAClC;AAAA,QACN;AAAA,QACA,YAAY,IAAI,KAAK,MAAM,UAAU,EAAE,QAAQ;AAAA,MACjD,EAAE;AAAA,MACF,EAAE,OAAO,CAAC,OAAO,EAAE;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,MAAc,gBACZ,SACA,QACe;AACf,UAAM,EAAE,OAAO,KAAK,IAAI,KAAK;AAC7B,UAAM,iBAAqC,CAAC;AAC5C,qBAAiB,KAAK,KAAK;AAAA,MACzB,gCAAgC,KAAK,IAAI,IAAI;AAAA,MAC7C;AAAA,IACF,GAAG;AACD,qBAAe,KAAK,CAAC;AAAA,IACvB;AAEA,UAAM,WAAqD,CAAC;AAC5D,eAAW,cAAc,gBAAgB;AACvC,cAAQ,eAAe;AACvB,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB,gCAAgC,KAAK,IAAI,IAAI,gBAAgB,WAAW,EAAE;AAAA,QAC1E;AAAA,MACF;AACA,YAAM,YAAY,IAAI,KAAK,WAAW,UAAU,EAAE,QAAQ;AAC1D,YAAM,kBAAkB,IAAI,KAAK,CAAC,GAAG,aACjC,IAAI,KAAK,IAAI,KAAK,CAAC,EAAE,UAAU,EAAE,QAAQ,IACzC;AACJ,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,IAAI,OAAO,WAAW,EAAE;AAAA,QACxB,YAAY;AAAA,UACV,aAAa,WAAW;AAAA,UACxB,KAAK,WAAW;AAAA,UAChB,KAAK,WAAW;AAAA,UAChB,SAAS,WAAW,SAAS,SAAS;AAAA,UACtC,YAAY;AAAA,UACZ,eAAe,IAAI,KAAK,CAAC,GAAG,SAAS;AAAA,QACvC;AAAA,QACA,YAAY,KAAK,IAAI,WAAW,mBAAmB,CAAC;AAAA,MACtD,CAAC;AAAA,IACH;AAEA,UAAM,QAAQ,SAAS,UAAU,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC;AAAA,EAC5D;AAAA,EAEA,MAAc,aACZ,SACA,QACe;AACf,UAAM,EAAE,OAAO,KAAK,IAAI,KAAK;AAC7B,UAAM,cAA+B,CAAC;AACtC,qBAAiB,KAAK,KAAK;AAAA,MACzB,gCAAgC,KAAK,IAAI,IAAI;AAAA,MAC7C;AAAA,IACF,GAAG;AACD,kBAAY,KAAK,CAAC;AAAA,IACpB;AAEA,UAAM,QAAQ;AAAA,MACZ,YAAY,IAAI,CAAC,aAAa;AAAA,QAC5B,MAAM;AAAA,QACN,IAAI,OAAO,QAAQ,EAAE;AAAA,QACrB,YAAY;AAAA,UACV,UAAU,QAAQ;AAAA,UAClB,MAAM,QAAQ,QAAQ;AAAA,UACtB,OAAO,QAAQ;AAAA,UACf,YAAY,QAAQ;AAAA,UACpB,YAAY,IAAI,KAAK,QAAQ,UAAU,EAAE,QAAQ;AAAA,UACjD,cAAc,QAAQ,eAClB,IAAI,KAAK,QAAQ,YAAY,EAAE,QAAQ,IACvC;AAAA,UACJ,QAAQ,QAAQ,OAAO;AAAA,QACzB;AAAA,QACA,YAAY,IAAI;AAAA,UACd,QAAQ,gBAAgB,QAAQ;AAAA,QAClC,EAAE,QAAQ;AAAA,MACZ,EAAE;AAAA,MACF,EAAE,OAAO,CAAC,SAAS,EAAE;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,MAAc,iBACZ,SACA,QACe;AACf,UAAM,EAAE,OAAO,KAAK,IAAI,KAAK;AAE7B,UAAM,eAAe,MAAM,KAAK;AAAA,MAC9B,OAAO,QAAQ;AACb,cAAM,MAAM,MAAM,KAAK;AAAA,UACrB,gCAAgC,KAAK,IAAI,IAAI;AAAA,UAC7C;AAAA,QACF;AACA,YAAI,IAAI,WAAW,KAAK;AACtB,iBAAO,EAAE,QAAQ,QAAQ;AAAA,QAC3B;AACA,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,OAAQ,IAAI,QAAQ,CAAC;AAAA,QACvB;AAAA,MACF;AAAA,MACA,EAAE,aAAa,IAAI,gBAAgB,KAAM,YAAY,KAAO,OAAO;AAAA,IACrE;AAEA,QAAI,CAAC,gBAAgB,aAAa,WAAW,GAAG;AAC9C,UAAI,CAAC,cAAc;AACjB,gBAAQ;AAAA,UACN;AAAA,QACF;AAAA,MACF;AACA;AAAA,IACF;AAEA,UAAM,QAAQ;AAAA,MACZ,aAAa,IAAI,CAAC,MAAM;AACtB,cAAM,YAAY,EAAE,MAAM,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,GAAG,CAAC;AACzD,cAAM,YAAY,EAAE,MAAM,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,GAAG,CAAC;AACzD,cAAM,aAAa,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC;AAC7D,eAAO;AAAA,UACL,MAAM;AAAA,UACN,IAAI,EAAE,OAAO;AAAA,UACb,YAAY;AAAA,YACV,SAAS,EAAE;AAAA,YACX;AAAA,YACA;AAAA,YACA,kBAAkB,aAAa,WAAW,IAAI,MAAO;AAAA,UACvD;AAAA,UACA,YAAY,aAAa,WAAW,IAAI,MAAO;AAAA,QACjD;AAAA,MACF,CAAC;AAAA,MACD,EAAE,OAAO,CAAC,aAAa,EAAE;AAAA,IAC3B;AAAA,EACF;AAAA,EAEA,MAAc,cACZ,SACA,QACe;AACf,UAAM,EAAE,OAAO,KAAK,IAAI,KAAK;AAC7B,UAAM,MAAM,MAAM,KAAK;AAAA,MACrB,gCAAgC,KAAK,IAAI,IAAI;AAAA,MAC7C;AAAA,IACF;AACA,UAAM,QAAQ;AAAA,MACZ;AAAA,QACE;AAAA,UACE,MAAM;AAAA,UACN,IAAI,GAAG,KAAK,IAAI,IAAI;AAAA,UACpB,YAAY;AAAA,YACV,OAAO,IAAI,KAAK;AAAA,YAChB,OAAO,IAAI,KAAK;AAAA,YAChB,UAAU,IAAI,KAAK;AAAA,UACrB;AAAA,UACA,YAAY,KAAK,IAAI;AAAA,QACvB;AAAA,MACF;AAAA,MACA,EAAE,OAAO,CAAC,MAAM,EAAE;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,MAAM,KACJ,SACA,SACA,QACe;AACf,UAAM,KAAK,cAAc,SAAS,MAAM;AACxC,UAAM,KAAK,iBAAiB,SAAS,SAAS,MAAM;AACpD,UAAM,KAAK,iBAAiB,SAAS,MAAM;AAC3C,UAAM,KAAK,WAAW,SAAS,SAAS,MAAM;AAC9C,UAAM,KAAK,gBAAgB,SAAS,MAAM;AAC1C,UAAM,KAAK,aAAa,SAAS,MAAM;AACvC,UAAM,KAAK,iBAAiB,SAAS,MAAM;AAAA,EAC7C;AACF;","names":["err"]}
|
|
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/pagination.ts","../src/github-actions.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","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, 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;\n\nexport interface RequestOptions {\n fetch?: FetchLike;\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 (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 const githubRateLimit: RateLimitPolicy = {\n parse(h) {\n const remainingRaw = h.get('x-ratelimit-remaining');\n const resetRaw = h.get('x-ratelimit-reset');\n if (remainingRaw === null || resetRaw === null) {\n return null;\n }\n const remaining = Number(remainingRaw);\n const reset = Number(resetRaw);\n if (!Number.isFinite(remaining) || !Number.isFinite(reset) || reset < 0) {\n return null;\n }\n return { remaining, resetAt: new Date(reset * 1000) };\n },\n};\n\nexport const sentryRateLimit: RateLimitPolicy = {\n parse(h) {\n const concurrent = h.get('x-sentry-rate-limit-remaining');\n const reset = h.get('x-sentry-rate-limit-reset');\n if (concurrent === null || reset === null) {\n return null;\n }\n const remaining = Number(concurrent);\n const resetSec = Number(reset);\n if (\n !Number.isFinite(remaining) ||\n !Number.isFinite(resetSec) ||\n resetSec < 0\n ) {\n return null;\n }\n return { remaining, resetAt: new Date(resetSec * 1000) };\n },\n};\n\nexport const linearRateLimit: RateLimitPolicy = {\n parse(h) {\n const remainingRaw = h.get('x-ratelimit-requests-remaining');\n const resetRaw = h.get('x-ratelimit-requests-reset');\n if (remainingRaw === null) {\n return null;\n }\n const remaining = Number(remainingRaw);\n if (!Number.isFinite(remaining)) {\n return null;\n }\n let resetAt: Date;\n if (resetRaw !== null) {\n const reset = Number(resetRaw);\n if (!Number.isFinite(reset) || reset < 0) {\n return null;\n }\n resetAt = new Date(reset);\n } else {\n resetAt = new Date(Date.now() + 60_000);\n }\n return { remaining, resetAt };\n },\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): AsyncIterable<T> {\n let next: string | null = initial.url;\n while (next) {\n const res: Awaited<ReturnType<typeof request>> = await request({\n ...initial,\n url: next,\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): AsyncIterable<T> {\n let req: HttpRequest = initial;\n while (true) {\n const res = await request(req);\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): AsyncIterable<T> {\n let page = 1;\n while (true) {\n const req = page === 1 ? initial : buildPage(initial, page);\n const res = await request(req);\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","import {\n type HttpRequest,\n type HttpResponse,\n githubRateLimit,\n parseLinkHeader,\n request,\n} from '@rawdash/connector-shared';\nimport {\n BaseConnector,\n type CredentialsSchema,\n type StorageHandle,\n type SyncOptions,\n type SyncResult,\n defineConfigFields,\n} from '@rawdash/core';\nimport { z } from 'zod';\n\nexport const configFields = defineConfigFields(\n z.object({\n owner: z.string().min(1).meta({\n label: 'Repository owner',\n description: 'GitHub username or organization name.',\n placeholder: 'rawdash',\n }),\n repo: z.string().min(1).meta({\n label: 'Repository',\n description: 'Repository name.',\n placeholder: 'rawdash',\n }),\n token: z.object({ $secret: z.string() }).optional().meta({\n label: 'Personal access token',\n description: 'GitHub PAT with `repo` scope.',\n secret: true,\n }),\n }),\n);\n\nexport interface GitHubActionsSettings {\n owner: string;\n repo: string;\n}\n\ninterface GitHubRunsResponse {\n workflow_runs: Array<{\n id: number;\n name: string;\n conclusion: string | null;\n status: string;\n head_branch: string | null;\n actor: { login: string } | null;\n created_at: string;\n updated_at: string;\n run_attempt: number;\n }>;\n}\n\ninterface GitHubPR {\n number: number;\n title: string;\n state: string;\n draft: boolean;\n user: { login: string };\n created_at: string;\n updated_at: string;\n}\n\ninterface GitHubReview {\n user: { login: string } | null;\n state: string;\n submitted_at: string;\n}\n\ninterface GitHubIssue {\n number: number;\n title: string;\n state: string;\n labels: Array<{ name: string }>;\n assignees: Array<{ login: string }>;\n user: { login: string };\n created_at: string;\n updated_at: string;\n closed_at: string | null;\n pull_request?: unknown;\n}\n\ninterface GitHubDeployment {\n id: number;\n environment: string;\n ref: string;\n sha: string;\n creator: { login: string } | null;\n created_at: string;\n}\n\ninterface GitHubDeploymentStatus {\n state: string;\n updated_at: string;\n}\n\ninterface GitHubRelease {\n id: number;\n tag_name: string;\n name: string | null;\n draft: boolean;\n prerelease: boolean;\n created_at: string;\n published_at: string | null;\n author: { login: string };\n}\n\ninterface GitHubContributorStats {\n total: number;\n weeks: Array<{ w: number; a: number; d: number; c: number }>;\n author: { login: string };\n}\n\ninterface GitHubRepo {\n stargazers_count: number;\n forks_count: number;\n subscribers_count: number;\n}\n\nconst githubCredentials = {\n token: {\n description: 'GitHub personal access token',\n auth: 'optional' as const,\n },\n} satisfies CredentialsSchema;\n\ntype GitHubCredentials = typeof githubCredentials;\n\ntype GitHubSyncPhase =\n | 'repo_stats'\n | 'workflow_runs'\n | 'pull_requests'\n | 'issues'\n | 'deployments'\n | 'releases'\n | 'contributors';\n\ninterface GitHubSyncCursor {\n phase: GitHubSyncPhase;\n pageUrl?: string;\n}\n\nconst PHASE_ORDER: readonly GitHubSyncPhase[] = [\n 'repo_stats',\n 'workflow_runs',\n 'pull_requests',\n 'issues',\n 'deployments',\n 'releases',\n 'contributors',\n];\n\ntype PhaseResult = { done: true } | { done: false; pageUrl: string };\n\nfunction isGitHubSyncCursor(value: unknown): value is GitHubSyncCursor {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const v = value as { phase?: unknown; pageUrl?: unknown };\n if (typeof v.phase !== 'string') {\n return false;\n }\n if (!(PHASE_ORDER as readonly string[]).includes(v.phase)) {\n return false;\n }\n if (v.pageUrl !== undefined && typeof v.pageUrl !== 'string') {\n return false;\n }\n return true;\n}\n\nexport class GitHubActionsConnector extends BaseConnector<\n GitHubActionsSettings,\n GitHubCredentials\n> {\n static readonly id = 'github-actions';\n\n static create(input: unknown): GitHubActionsConnector {\n const parsed = configFields.parse(input);\n return new GitHubActionsConnector(\n { owner: parsed.owner, repo: parsed.repo },\n { token: parsed.token },\n );\n }\n\n readonly id = 'github-actions';\n\n override readonly credentials = githubCredentials;\n\n private buildHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n Accept: 'application/vnd.github+json',\n 'X-GitHub-Api-Version': '2022-11-28',\n 'User-Agent': 'rawdash/connector-github (+https://rawdash.dev)',\n };\n if (this.creds.token) {\n headers['Authorization'] = `Bearer ${this.creds.token}`;\n }\n return headers;\n }\n\n private get<T>(url: string, signal?: AbortSignal): Promise<HttpResponse<T>> {\n const req: HttpRequest = {\n url,\n headers: this.buildHeaders(),\n signal,\n rateLimit: githubRateLimit,\n };\n return request<T>(req);\n }\n\n private async syncRepoStats(\n storage: StorageHandle,\n initialPageUrl: string | undefined,\n signal?: AbortSignal,\n ): Promise<PhaseResult> {\n const { owner, repo } = this.settings;\n const url =\n initialPageUrl ?? `https://api.github.com/repos/${owner}/${repo}`;\n const res = await this.get<GitHubRepo>(url, signal);\n await storage.entities(\n [\n {\n type: 'repo',\n id: `${owner}/${repo}`,\n attributes: {\n stars: res.body.stargazers_count,\n forks: res.body.forks_count,\n watchers: res.body.subscribers_count,\n },\n updated_at: Date.now(),\n },\n ],\n { types: ['repo'] },\n );\n return { done: true };\n }\n\n private async syncWorkflowRunsLatest(\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<PhaseResult> {\n const { owner, repo } = this.settings;\n const res = await this.get<GitHubRunsResponse>(\n `https://api.github.com/repos/${owner}/${repo}/actions/runs?per_page=1`,\n signal,\n );\n const run = res.body.workflow_runs[0];\n if (run) {\n await storage.event({\n name: 'workflow_run',\n start_ts: new Date(run.created_at).getTime(),\n end_ts: new Date(run.updated_at).getTime(),\n attributes: {\n id: run.id,\n workflow_name: run.name,\n conclusion: run.conclusion ?? 'unknown',\n status: run.status,\n branch: run.head_branch ?? '',\n actor: run.actor?.login ?? '',\n run_attempt: run.run_attempt,\n },\n });\n }\n return { done: true };\n }\n\n private async syncWorkflowRunsFull(\n storage: StorageHandle,\n options: SyncOptions,\n initialPageUrl: string | undefined,\n signal?: AbortSignal,\n ): Promise<PhaseResult> {\n const { owner, repo } = this.settings;\n const cutoff = options.since ? new Date(options.since).getTime() : null;\n\n if (initialPageUrl === undefined) {\n await storage.events([], { names: ['workflow_run'] });\n }\n\n let nextUrl: string | null =\n initialPageUrl ??\n `https://api.github.com/repos/${owner}/${repo}/actions/runs?per_page=100`;\n\n while (nextUrl) {\n if (signal?.aborted) {\n return { done: false, pageUrl: nextUrl };\n }\n const res: HttpResponse<GitHubRunsResponse> =\n await this.get<GitHubRunsResponse>(nextUrl, signal);\n const body = res.body;\n const nextLink = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n const runs = body.workflow_runs;\n if (runs.length === 0) {\n return { done: true };\n }\n\n const pageEvents = runs\n .filter((run) => {\n if (cutoff === null) {\n return true;\n }\n const createdMs = new Date(run.created_at).getTime();\n const updatedMs = new Date(run.updated_at).getTime();\n return !(createdMs < cutoff && updatedMs < cutoff);\n })\n .map((run) => ({\n name: 'workflow_run',\n start_ts: new Date(run.created_at).getTime(),\n end_ts: new Date(run.updated_at).getTime(),\n attributes: {\n id: run.id,\n workflow_name: run.name,\n conclusion: run.conclusion ?? 'unknown',\n status: run.status,\n branch: run.head_branch ?? '',\n actor: run.actor?.login ?? '',\n run_attempt: run.run_attempt,\n },\n }));\n\n for (const e of pageEvents) {\n await storage.event(e);\n }\n\n const lastRun = runs.at(-1)!;\n if (\n cutoff !== null &&\n new Date(lastRun.created_at).getTime() < cutoff &&\n new Date(lastRun.updated_at).getTime() < cutoff\n ) {\n return { done: true };\n }\n\n nextUrl = nextLink;\n }\n\n return { done: true };\n }\n\n private async syncPullRequests(\n storage: StorageHandle,\n initialPageUrl: string | undefined,\n signal?: AbortSignal,\n ): Promise<PhaseResult> {\n const { owner, repo } = this.settings;\n\n if (initialPageUrl === undefined) {\n await storage.entities([], { types: ['pull_request'] });\n await storage.edges([], { kinds: ['reviewed_by'] });\n }\n\n let nextUrl: string | null =\n initialPageUrl ??\n `https://api.github.com/repos/${owner}/${repo}/pulls?state=all&per_page=100`;\n\n while (nextUrl) {\n if (signal?.aborted) {\n return { done: false, pageUrl: nextUrl };\n }\n const res: HttpResponse<GitHubPR[]> = await this.get<GitHubPR[]>(\n nextUrl,\n signal,\n );\n const body = res.body;\n const nextLink = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n if (body.length === 0) {\n return { done: true };\n }\n\n for (const pr of body) {\n await storage.entity({\n type: 'pull_request',\n id: String(pr.number),\n attributes: {\n title: pr.title,\n state: pr.state,\n draft: pr.draft,\n author: pr.user.login,\n created_at: new Date(pr.created_at).getTime(),\n },\n updated_at: new Date(pr.updated_at).getTime(),\n });\n }\n\n for (const pr of body) {\n if (signal?.aborted) {\n return { done: false, pageUrl: nextUrl };\n }\n const res = await this.get<GitHubReview[]>(\n `https://api.github.com/repos/${owner}/${repo}/pulls/${pr.number}/reviews`,\n signal,\n );\n for (const review of res.body) {\n if (!review.user) {\n continue;\n }\n await storage.edge({\n from_type: 'pull_request',\n from_id: String(pr.number),\n kind: 'reviewed_by',\n to_type: 'user',\n to_id: review.user.login,\n attributes: { state: review.state },\n updated_at: new Date(review.submitted_at).getTime(),\n });\n }\n }\n\n nextUrl = nextLink;\n }\n\n return { done: true };\n }\n\n private async syncIssues(\n storage: StorageHandle,\n options: SyncOptions,\n initialPageUrl: string | undefined,\n signal?: AbortSignal,\n ): Promise<PhaseResult> {\n const { owner, repo } = this.settings;\n\n if (initialPageUrl === undefined) {\n await storage.entities([], { types: ['issue'] });\n }\n\n let nextUrl: string | null;\n if (initialPageUrl) {\n nextUrl = initialPageUrl;\n } else {\n const url = new URL(\n `https://api.github.com/repos/${owner}/${repo}/issues`,\n );\n url.searchParams.set('state', 'all');\n url.searchParams.set('per_page', '100');\n if (options.since) {\n url.searchParams.set('since', options.since);\n }\n nextUrl = url.toString();\n }\n\n while (nextUrl) {\n if (signal?.aborted) {\n return { done: false, pageUrl: nextUrl };\n }\n const res: HttpResponse<GitHubIssue[]> = await this.get<GitHubIssue[]>(\n nextUrl,\n signal,\n );\n const body = res.body;\n const nextLink = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n if (body.length === 0) {\n return { done: true };\n }\n\n for (const issue of body) {\n if (issue.pull_request !== undefined) {\n continue;\n }\n await storage.entity({\n type: 'issue',\n id: String(issue.number),\n attributes: {\n number: issue.number,\n title: issue.title,\n state: issue.state,\n labels: issue.labels.map((l) => l.name),\n assignees: issue.assignees.map((a) => a.login),\n author: issue.user.login,\n created_at: new Date(issue.created_at).getTime(),\n updated_at: new Date(issue.updated_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 nextUrl = nextLink;\n }\n\n return { done: true };\n }\n\n private async syncDeployments(\n storage: StorageHandle,\n initialPageUrl: string | undefined,\n signal?: AbortSignal,\n ): Promise<PhaseResult> {\n const { owner, repo } = this.settings;\n\n if (initialPageUrl === undefined) {\n await storage.entities([], { types: ['deployment'] });\n }\n\n let nextUrl: string | null =\n initialPageUrl ??\n `https://api.github.com/repos/${owner}/${repo}/deployments?per_page=100`;\n\n while (nextUrl) {\n if (signal?.aborted) {\n return { done: false, pageUrl: nextUrl };\n }\n const res: HttpResponse<GitHubDeployment[]> = await this.get<\n GitHubDeployment[]\n >(nextUrl, signal);\n const body = res.body;\n const nextLink = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n if (body.length === 0) {\n return { done: true };\n }\n\n for (const deployment of body) {\n if (signal?.aborted) {\n return { done: false, pageUrl: nextUrl };\n }\n const res = await this.get<GitHubDeploymentStatus[]>(\n `https://api.github.com/repos/${owner}/${repo}/deployments/${deployment.id}/statuses?per_page=1`,\n signal,\n );\n const createdMs = new Date(deployment.created_at).getTime();\n const statusUpdatedMs = res.body[0]?.updated_at\n ? new Date(res.body[0].updated_at).getTime()\n : null;\n await storage.entity({\n type: 'deployment',\n id: String(deployment.id),\n attributes: {\n environment: deployment.environment,\n ref: deployment.ref,\n sha: deployment.sha,\n creator: deployment.creator?.login ?? '',\n created_at: createdMs,\n latest_status: res.body[0]?.state ?? 'unknown',\n },\n updated_at: Math.max(createdMs, statusUpdatedMs ?? 0),\n });\n }\n\n nextUrl = nextLink;\n }\n\n return { done: true };\n }\n\n private async syncReleases(\n storage: StorageHandle,\n initialPageUrl: string | undefined,\n signal?: AbortSignal,\n ): Promise<PhaseResult> {\n const { owner, repo } = this.settings;\n\n if (initialPageUrl === undefined) {\n await storage.entities([], { types: ['release'] });\n }\n\n let nextUrl: string | null =\n initialPageUrl ??\n `https://api.github.com/repos/${owner}/${repo}/releases?per_page=100`;\n\n while (nextUrl) {\n if (signal?.aborted) {\n return { done: false, pageUrl: nextUrl };\n }\n const res: HttpResponse<GitHubRelease[]> = await this.get<\n GitHubRelease[]\n >(nextUrl, signal);\n const body = res.body;\n const nextLink = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n if (body.length === 0) {\n return { done: true };\n }\n\n for (const release of body) {\n await storage.entity({\n type: 'release',\n id: String(release.id),\n attributes: {\n tag_name: release.tag_name,\n name: release.name ?? '',\n draft: release.draft,\n prerelease: release.prerelease,\n created_at: new Date(release.created_at).getTime(),\n published_at: release.published_at\n ? new Date(release.published_at).getTime()\n : null,\n author: release.author.login,\n },\n updated_at: new Date(\n release.published_at ?? release.created_at,\n ).getTime(),\n });\n }\n\n nextUrl = nextLink;\n }\n\n return { done: true };\n }\n\n private async syncContributors(\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<PhaseResult> {\n const { owner, repo } = this.settings;\n\n const contributors = await this.withRetry<GitHubContributorStats[]>(\n async (sig) => {\n const res = await this.get<GitHubContributorStats[] | null>(\n `https://api.github.com/repos/${owner}/${repo}/stats/contributors`,\n sig,\n );\n if (res.status === 202) {\n return { status: 'retry' };\n }\n return {\n status: 'done',\n value: (res.body ?? []) as GitHubContributorStats[],\n };\n },\n { maxAttempts: 15, initialDelayMs: 1000, maxDelayMs: 10000, signal },\n );\n\n if (!contributors || contributors.length === 0) {\n if (!contributors) {\n console.warn(\n '[github-actions] Stats endpoint never became ready — skipping contributor sync and keeping previous data.',\n );\n }\n return { done: true };\n }\n\n await storage.entities(\n contributors.map((c) => {\n const additions = c.weeks.reduce((sum, w) => sum + w.a, 0);\n const deletions = c.weeks.reduce((sum, w) => sum + w.d, 0);\n const latestWeek = [...c.weeks].reverse().find((w) => w.c > 0);\n return {\n type: 'contributor',\n id: c.author.login,\n attributes: {\n commits: c.total,\n additions,\n deletions,\n latest_commit_at: latestWeek ? latestWeek.w * 1000 : null,\n },\n updated_at: latestWeek ? latestWeek.w * 1000 : 0,\n };\n }),\n { types: ['contributor'] },\n );\n return { done: true };\n }\n\n private sanitizeIncomingCursor(\n cursor: unknown,\n ): GitHubSyncCursor | undefined {\n if (!isGitHubSyncCursor(cursor)) {\n return undefined;\n }\n if (cursor.pageUrl === undefined) {\n return cursor;\n }\n const { owner, repo } = this.settings;\n const expectedPrefix = `/repos/${owner}/${repo}/`;\n try {\n const u = new URL(cursor.pageUrl);\n if (\n u.protocol !== 'https:' ||\n u.host !== 'api.github.com' ||\n !u.pathname.startsWith(expectedPrefix)\n ) {\n return { phase: cursor.phase };\n }\n return cursor;\n } catch {\n return { phase: cursor.phase };\n }\n }\n\n private async runPhase(\n phase: GitHubSyncPhase,\n storage: StorageHandle,\n options: SyncOptions,\n initialPageUrl: string | undefined,\n signal?: AbortSignal,\n ): Promise<PhaseResult> {\n switch (phase) {\n case 'repo_stats':\n return this.syncRepoStats(storage, initialPageUrl, signal);\n case 'workflow_runs':\n return options.mode === 'latest'\n ? this.syncWorkflowRunsLatest(storage, signal)\n : this.syncWorkflowRunsFull(storage, options, initialPageUrl, signal);\n case 'pull_requests':\n return this.syncPullRequests(storage, initialPageUrl, signal);\n case 'issues':\n return this.syncIssues(storage, options, initialPageUrl, signal);\n case 'deployments':\n return this.syncDeployments(storage, initialPageUrl, signal);\n case 'releases':\n return this.syncReleases(storage, initialPageUrl, signal);\n case 'contributors':\n return this.syncContributors(storage, signal);\n }\n }\n\n async sync(\n options: SyncOptions,\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<SyncResult> {\n const incoming = this.sanitizeIncomingCursor(options.cursor);\n const startIdx = incoming ? PHASE_ORDER.indexOf(incoming.phase) : 0;\n\n for (let i = startIdx; i < PHASE_ORDER.length; i++) {\n const phase = PHASE_ORDER[i]!;\n const initialPageUrl = i === startIdx ? incoming?.pageUrl : undefined;\n const result = await this.runPhase(\n phase,\n storage,\n options,\n initialPageUrl,\n signal,\n );\n if (!result.done) {\n return {\n done: false,\n cursor: { phase, pageUrl: result.pageUrl } satisfies GitHubSyncCursor,\n };\n }\n }\n\n return { done: true };\n }\n}\n"],"mappings":"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","names":["err","res"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rawdash/connector-github",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Rawdash connector for GitHub Actions",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"zod": "^4.4.3",
|
|
26
|
-
"@rawdash/core": "0.
|
|
26
|
+
"@rawdash/core": "0.8.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"tsup": "^8.0.0",
|