@rawdash/connector-github 0.21.1 → 0.22.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 CHANGED
@@ -89,6 +89,11 @@ declare const githubResources: {
89
89
  readonly description: "Open and closed pull requests, including draft state, author, and review state.";
90
90
  readonly endpoint: "GET /repos/{owner}/{repo}/pulls";
91
91
  readonly notes: "Review state is folded in from GET /repos/{owner}/{repo}/pulls/{number}/reviews per PR.";
92
+ readonly filterable: [{
93
+ readonly field: "state";
94
+ readonly ops: ["eq"];
95
+ readonly values: ["open", "closed", "merged"];
96
+ }];
92
97
  readonly responses: {
93
98
  readonly pull_requests: z.ZodArray<z.ZodObject<{
94
99
  number: z.ZodNumber;
@@ -161,6 +166,11 @@ declare const githubResources: {
161
166
  readonly shape: "entity";
162
167
  readonly description: "Open and closed issues with labels, assignees, and author (pull requests excluded).";
163
168
  readonly endpoint: "GET /repos/{owner}/{repo}/issues";
169
+ readonly filterable: [{
170
+ readonly field: "state";
171
+ readonly ops: ["eq"];
172
+ readonly values: ["open", "closed"];
173
+ }];
164
174
  readonly responses: {
165
175
  readonly issues: z.ZodArray<z.ZodObject<{
166
176
  number: z.ZodNumber;
@@ -375,6 +385,11 @@ declare class GitHubConnector extends BaseConnector<GitHubSettings, GitHubCreden
375
385
  readonly description: "Open and closed pull requests, including draft state, author, and review state.";
376
386
  readonly endpoint: "GET /repos/{owner}/{repo}/pulls";
377
387
  readonly notes: "Review state is folded in from GET /repos/{owner}/{repo}/pulls/{number}/reviews per PR.";
388
+ readonly filterable: [{
389
+ readonly field: "state";
390
+ readonly ops: ["eq"];
391
+ readonly values: ["open", "closed", "merged"];
392
+ }];
378
393
  readonly responses: {
379
394
  readonly pull_requests: z.ZodArray<z.ZodObject<{
380
395
  number: z.ZodNumber;
@@ -447,6 +462,11 @@ declare class GitHubConnector extends BaseConnector<GitHubSettings, GitHubCreden
447
462
  readonly shape: "entity";
448
463
  readonly description: "Open and closed issues with labels, assignees, and author (pull requests excluded).";
449
464
  readonly endpoint: "GET /repos/{owner}/{repo}/issues";
465
+ readonly filterable: [{
466
+ readonly field: "state";
467
+ readonly ops: ["eq"];
468
+ readonly values: ["open", "closed"];
469
+ }];
450
470
  readonly responses: {
451
471
  readonly issues: z.ZodArray<z.ZodObject<{
452
472
  number: z.ZodNumber;
@@ -843,6 +863,8 @@ declare class GitHubConnector extends BaseConnector<GitHubSettings, GitHubCreden
843
863
  private sanitizePageUrl;
844
864
  private isResourceAllowed;
845
865
  private resolveCursor;
866
+ private specsForResource;
867
+ private specCutoff;
846
868
  private fetchRepoStats;
847
869
  private fetchWorkflowRunsLatest;
848
870
  private fetchWorkflowRunsFull;
package/dist/index.js CHANGED
@@ -80,6 +80,7 @@ import {
80
80
  makeChunkedCursorGuard,
81
81
  paginateChunked,
82
82
  resolveBackfillCutoff,
83
+ resolveSpecCutoff,
83
84
  schemasFromResources
84
85
  } from "@rawdash/core";
85
86
  import { z } from "zod";
@@ -109,6 +110,7 @@ var doc = defineConnectorDoc({
109
110
  tagline: "Sync pull requests, issues, deployments, releases, CI runs, and contributor activity from a GitHub repository.",
110
111
  vendor: {
111
112
  name: "GitHub",
113
+ domain: "github.com",
112
114
  apiDocs: "https://docs.github.com/rest",
113
115
  website: "https://github.com"
114
116
  },
@@ -155,6 +157,24 @@ var PHASE_RESOURCES = {
155
157
  releases: ["release"],
156
158
  contributors: ["contributor"]
157
159
  };
160
+ var SINGLE_SPEC_PHASES = /* @__PURE__ */ new Set([
161
+ "repo_stats",
162
+ "contributors"
163
+ ]);
164
+ function pushableState(filter) {
165
+ if (!filter) {
166
+ return null;
167
+ }
168
+ for (const clause of filter) {
169
+ if ("field" in clause && clause.field === "state" && clause.op === "eq") {
170
+ const { value } = clause;
171
+ if (value === "open" || value === "closed") {
172
+ return value;
173
+ }
174
+ }
175
+ }
176
+ return null;
177
+ }
158
178
  function selectPhases(allowlist) {
159
179
  if (allowlist === void 0) {
160
180
  return PHASE_ORDER;
@@ -439,6 +459,9 @@ var githubResources = defineResources({
439
459
  description: "Open and closed pull requests, including draft state, author, and review state.",
440
460
  endpoint: "GET /repos/{owner}/{repo}/pulls",
441
461
  notes: "Review state is folded in from GET /repos/{owner}/{repo}/pulls/{number}/reviews per PR.",
462
+ filterable: [
463
+ { field: "state", ops: ["eq"], values: ["open", "closed", "merged"] }
464
+ ],
442
465
  responses: {
443
466
  pull_requests: pullRequestsSchema,
444
467
  pull_request_reviews: reviewsSchema
@@ -448,6 +471,7 @@ var githubResources = defineResources({
448
471
  shape: "entity",
449
472
  description: "Open and closed issues with labels, assignees, and author (pull requests excluded).",
450
473
  endpoint: "GET /repos/{owner}/{repo}/issues",
474
+ filterable: [{ field: "state", ops: ["eq"], values: ["open", "closed"] }],
451
475
  responses: { issues: issuesSchema }
452
476
  },
453
477
  deployment: {
@@ -568,9 +592,20 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
568
592
  }
569
593
  return {
570
594
  phase: cursor.phase,
571
- page: this.sanitizePageUrl(cursor.phase, cursor.page)
595
+ page: this.sanitizePageUrl(cursor.phase, cursor.page),
596
+ spec: cursor.spec
572
597
  };
573
598
  }
599
+ specsForResource(options, resource) {
600
+ const specs = options.fetchSpecs?.[resource];
601
+ return specs && specs.length > 0 ? specs : [{}];
602
+ }
603
+ specCutoff(options, resource, spec, now) {
604
+ if (options.fetchSpecs?.[resource]) {
605
+ return resolveSpecCutoff(spec.requiredWindowMs, now);
606
+ }
607
+ return resolveBackfillCutoff(options, resource, now);
608
+ }
574
609
  async fetchRepoStats(signal) {
575
610
  const { owner, repo } = this.settings;
576
611
  const res = await this.fetch(
@@ -590,7 +625,7 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
590
625
  const run = res.body.workflow_runs[0];
591
626
  return { items: run ? [run] : [], next: null };
592
627
  }
593
- async fetchWorkflowRunsFull(options, page, signal) {
628
+ async fetchWorkflowRunsFull(page, signal, cutoff) {
594
629
  const { owner, repo } = this.settings;
595
630
  const url = page ?? `https://api.github.com/repos/${owner}/${repo}/actions/runs?per_page=100`;
596
631
  const res = await this.fetch(
@@ -600,7 +635,6 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
600
635
  );
601
636
  const nextLink = parseLinkHeader(res.headers.get("link"))["next"] ?? null;
602
637
  const runs = res.body.workflow_runs;
603
- const cutoff = resolveBackfillCutoff(options, "workflow_run", Date.now());
604
638
  const filtered = runs.filter((run) => {
605
639
  if (cutoff === null) {
606
640
  return true;
@@ -616,13 +650,13 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
616
650
  next: cutoffReached ? null : nextLink
617
651
  };
618
652
  }
619
- async fetchPullRequests(options, page, signal) {
653
+ async fetchPullRequests(options, page, signal, spec, cutoff) {
620
654
  const { owner, repo } = this.settings;
621
- const url = page ?? `https://api.github.com/repos/${owner}/${repo}/pulls?state=all&sort=updated&direction=desc&per_page=100`;
655
+ const state = pushableState(spec.filter) ?? "all";
656
+ const url = page ?? `https://api.github.com/repos/${owner}/${repo}/pulls?state=${state}&sort=updated&direction=desc&per_page=100`;
622
657
  const res = await this.fetch(url, "pull_requests", signal);
623
658
  const nextLink = parseLinkHeader(res.headers.get("link"))["next"] ?? null;
624
659
  const prs = res.body;
625
- const cutoff = resolveBackfillCutoff(options, "pull_request", Date.now());
626
660
  const filteredPrs = cutoff !== null ? prs.filter((pr) => new Date(pr.updated_at).getTime() >= cutoff) : prs;
627
661
  const lastPr = prs.at(-1);
628
662
  const cutoffReached = cutoff !== null && lastPr !== void 0 && new Date(lastPr.updated_at).getTime() < cutoff;
@@ -641,16 +675,15 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
641
675
  const items = [{ prs: filteredPrs, reviewsByPR }];
642
676
  return { items, next: cutoffReached ? null : nextLink };
643
677
  }
644
- async fetchIssues(options, page, signal) {
678
+ async fetchIssues(page, signal, spec, cutoff) {
645
679
  const { owner, repo } = this.settings;
646
680
  let url;
647
681
  if (page) {
648
682
  url = page;
649
683
  } else {
650
684
  const u = new URL(`https://api.github.com/repos/${owner}/${repo}/issues`);
651
- u.searchParams.set("state", "all");
685
+ u.searchParams.set("state", pushableState(spec.filter) ?? "all");
652
686
  u.searchParams.set("per_page", "100");
653
- const cutoff = resolveBackfillCutoff(options, "issue", Date.now());
654
687
  if (cutoff !== null) {
655
688
  u.searchParams.set("since", new Date(cutoff).toISOString());
656
689
  }
@@ -660,7 +693,7 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
660
693
  const nextLink = parseLinkHeader(res.headers.get("link"))["next"] ?? null;
661
694
  return { items: res.body, next: nextLink };
662
695
  }
663
- async fetchDeployments(options, page, signal) {
696
+ async fetchDeployments(options, page, signal, cutoff) {
664
697
  const { owner, repo } = this.settings;
665
698
  const url = page ?? `https://api.github.com/repos/${owner}/${repo}/deployments?per_page=100`;
666
699
  const res = await this.fetch(
@@ -670,7 +703,6 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
670
703
  );
671
704
  const nextLink = parseLinkHeader(res.headers.get("link"))["next"] ?? null;
672
705
  const deployments = res.body;
673
- const cutoff = resolveBackfillCutoff(options, "deployment", Date.now());
674
706
  const filteredDeployments = cutoff !== null ? deployments.filter((d) => new Date(d.created_at).getTime() >= cutoff) : deployments;
675
707
  const lastDeployment = deployments.at(-1);
676
708
  const cutoffReached = cutoff !== null && lastDeployment !== void 0 && new Date(lastDeployment.created_at).getTime() < cutoff;
@@ -691,13 +723,12 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
691
723
  ];
692
724
  return { items, next: cutoffReached ? null : nextLink };
693
725
  }
694
- async fetchReleases(options, page, signal) {
726
+ async fetchReleases(page, signal, cutoff) {
695
727
  const { owner, repo } = this.settings;
696
728
  const url = page ?? `https://api.github.com/repos/${owner}/${repo}/releases?per_page=100`;
697
729
  const res = await this.fetch(url, "releases", signal);
698
730
  const nextLink = parseLinkHeader(res.headers.get("link"))["next"] ?? null;
699
731
  const releases = res.body;
700
- const cutoff = resolveBackfillCutoff(options, "release", Date.now());
701
732
  const filtered = cutoff !== null ? releases.filter((r) => {
702
733
  const ts = new Date(r.published_at ?? r.created_at).getTime();
703
734
  return ts >= cutoff;
@@ -775,8 +806,8 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
775
806
  }
776
807
  });
777
808
  }
778
- async writeWorkflowRunsFull(storage, items, page) {
779
- if (page === null) {
809
+ async writeWorkflowRunsFull(storage, items, page, spec) {
810
+ if (page === null && spec === 0) {
780
811
  await storage.events([], { names: ["workflow_run"] });
781
812
  this.seenWorkflowRunIds.clear();
782
813
  }
@@ -818,12 +849,12 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
818
849
  });
819
850
  }
820
851
  }
821
- async writePullRequests(storage, items, page, options) {
852
+ async writePullRequests(storage, items, page, spec, options) {
822
853
  const reviewsAllowed = this.isResourceAllowed(
823
854
  options,
824
855
  "pull_request_reviews"
825
856
  );
826
- if (page === null) {
857
+ if (page === null && spec === 0) {
827
858
  await storage.entities([], { types: ["pull_request"] });
828
859
  if (reviewsAllowed) {
829
860
  await storage.edges([], { kinds: ["reviewed_by"] });
@@ -872,8 +903,8 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
872
903
  }
873
904
  }
874
905
  }
875
- async writeIssues(storage, items, page) {
876
- if (page === null) {
906
+ async writeIssues(storage, items, page, spec) {
907
+ if (page === null && spec === 0) {
877
908
  await storage.entities([], { types: ["issue"] });
878
909
  }
879
910
  const issues = dedupeByKey(
@@ -900,12 +931,12 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
900
931
  });
901
932
  }
902
933
  }
903
- async writeDeployments(storage, items, page, options) {
934
+ async writeDeployments(storage, items, page, spec, options) {
904
935
  const statusesAllowed = this.isResourceAllowed(
905
936
  options,
906
937
  "deployment_statuses"
907
938
  );
908
- if (page === null) {
939
+ if (page === null && spec === 0) {
909
940
  if (!statusesAllowed) {
910
941
  const existing = await storage.queryEntities({ type: "deployment" });
911
942
  for (const entity of existing) {
@@ -951,8 +982,8 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
951
982
  }
952
983
  }
953
984
  }
954
- async writeReleases(storage, items, page) {
955
- if (page === null) {
985
+ async writeReleases(storage, items, page, spec) {
986
+ if (page === null && spec === 0) {
956
987
  await storage.entities([], { types: ["release"] });
957
988
  }
958
989
  const releases = dedupeByKey(
@@ -1011,43 +1042,76 @@ var GitHubConnector = class _GitHubConnector extends BaseConnector {
1011
1042
  async sync(options, storage, signal) {
1012
1043
  const cursor = this.resolveCursor(options.cursor);
1013
1044
  const phases = selectPhases(options.resources);
1045
+ const specCount = (phase) => {
1046
+ if (options.mode === "latest" || SINGLE_SPEC_PHASES.has(phase)) {
1047
+ return 1;
1048
+ }
1049
+ return this.specsForResource(options, PHASE_RESOURCES[phase][0]).length;
1050
+ };
1051
+ const specFor = (phase, specIndex) => this.specsForResource(options, PHASE_RESOURCES[phase][0])[specIndex] ?? {};
1052
+ const cutoffFor = (phase, spec) => this.specCutoff(options, PHASE_RESOURCES[phase][0], spec, Date.now());
1014
1053
  return paginateChunked({
1015
1054
  phases,
1016
1055
  cursor,
1017
1056
  signal,
1057
+ specCount,
1018
1058
  logger: this.logger,
1019
- fetchPage: async (phase, page, sig) => {
1059
+ fetchPage: async (phase, page, sig, specIndex) => {
1060
+ const spec = specFor(phase, specIndex);
1020
1061
  switch (phase) {
1021
1062
  case "repo_stats":
1022
1063
  return this.fetchRepoStats(sig);
1023
1064
  case "workflow_runs":
1024
- return options.mode === "latest" ? this.fetchWorkflowRunsLatest(sig) : this.fetchWorkflowRunsFull(options, page, sig);
1065
+ return options.mode === "latest" ? this.fetchWorkflowRunsLatest(sig) : this.fetchWorkflowRunsFull(page, sig, cutoffFor(phase, spec));
1025
1066
  case "pull_requests":
1026
- return this.fetchPullRequests(options, page, sig);
1067
+ return this.fetchPullRequests(
1068
+ options,
1069
+ page,
1070
+ sig,
1071
+ spec,
1072
+ cutoffFor(phase, spec)
1073
+ );
1027
1074
  case "issues":
1028
- return this.fetchIssues(options, page, sig);
1075
+ return this.fetchIssues(page, sig, spec, cutoffFor(phase, spec));
1029
1076
  case "deployments":
1030
- return this.fetchDeployments(options, page, sig);
1077
+ return this.fetchDeployments(
1078
+ options,
1079
+ page,
1080
+ sig,
1081
+ cutoffFor(phase, spec)
1082
+ );
1031
1083
  case "releases":
1032
- return this.fetchReleases(options, page, sig);
1084
+ return this.fetchReleases(page, sig, cutoffFor(phase, spec));
1033
1085
  case "contributors":
1034
1086
  return this.fetchContributors(sig);
1035
1087
  }
1036
1088
  },
1037
- writeBatch: async (phase, items, page) => {
1089
+ writeBatch: async (phase, items, page, specIndex) => {
1038
1090
  switch (phase) {
1039
1091
  case "repo_stats":
1040
1092
  return this.writeRepoStats(storage, items);
1041
1093
  case "workflow_runs":
1042
- return options.mode === "latest" ? this.writeWorkflowRunsLatest(storage, items) : this.writeWorkflowRunsFull(storage, items, page);
1094
+ return options.mode === "latest" ? this.writeWorkflowRunsLatest(storage, items) : this.writeWorkflowRunsFull(storage, items, page, specIndex);
1043
1095
  case "pull_requests":
1044
- return this.writePullRequests(storage, items, page, options);
1096
+ return this.writePullRequests(
1097
+ storage,
1098
+ items,
1099
+ page,
1100
+ specIndex,
1101
+ options
1102
+ );
1045
1103
  case "issues":
1046
- return this.writeIssues(storage, items, page);
1104
+ return this.writeIssues(storage, items, page, specIndex);
1047
1105
  case "deployments":
1048
- return this.writeDeployments(storage, items, page, options);
1106
+ return this.writeDeployments(
1107
+ storage,
1108
+ items,
1109
+ page,
1110
+ specIndex,
1111
+ options
1112
+ );
1049
1113
  case "releases":
1050
- return this.writeReleases(storage, items, page);
1114
+ return this.writeReleases(storage, items, page, specIndex);
1051
1115
  case "contributors":
1052
1116
  return this.writeContributors(storage, items);
1053
1117
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../connector-shared/src/errors.ts","../../../connector-shared/src/retry.ts","../../../connector-shared/src/version.ts","../../../connector-shared/src/request.ts","../../../connector-shared/src/rate-limit.ts","../../../connector-shared/src/map-concurrent.ts","../../../connector-shared/src/sanitize.ts","../../../connector-shared/src/epoch.ts","../../../connector-shared/src/pagination.ts","../../../connector-shared/src/logger.ts","../src/github.ts","../src/index.ts"],"sourcesContent":["import type { HttpResponse } from './types';\n\nexport type HttpErrorKind =\n | 'transient'\n | 'rate_limit'\n | 'auth'\n | 'upstream_bug'\n | 'client_bug';\n\nexport abstract class HttpClientError extends Error {\n abstract readonly kind: HttpErrorKind;\n readonly response?: HttpResponse;\n\n constructor(message: string, response?: HttpResponse) {\n super(message);\n this.name = new.target.name;\n this.response = response;\n }\n}\n\nexport class TransientError extends HttpClientError {\n readonly kind = 'transient' as const;\n}\n\nexport class RateLimitError extends HttpClientError {\n readonly kind = 'rate_limit' as const;\n readonly retryAfter?: Date;\n\n constructor(message: string, response?: HttpResponse, retryAfter?: Date) {\n super(message, response);\n this.retryAfter = retryAfter;\n }\n}\n\nexport class AuthError extends HttpClientError {\n readonly kind = 'auth' as const;\n}\n\nexport class UpstreamBugError extends HttpClientError {\n readonly kind = 'upstream_bug' as const;\n}\n\nexport class ClientBugError extends HttpClientError {\n readonly kind = 'client_bug' as const;\n}\n\nexport function classifyStatus(status: number): HttpErrorKind {\n if (status === 429) {\n return 'rate_limit';\n }\n if (status === 401 || status === 403) {\n return 'auth';\n }\n if (status === 408) {\n return 'transient';\n }\n if (status >= 500) {\n return 'upstream_bug';\n }\n if (status >= 400) {\n return 'client_bug';\n }\n return 'client_bug';\n}\n\nexport function errorForStatus(\n message: string,\n response: HttpResponse,\n retryAfter?: Date,\n): HttpClientError {\n const kind = classifyStatus(response.status);\n switch (kind) {\n case 'rate_limit':\n return new RateLimitError(message, response, retryAfter);\n case 'auth':\n return new AuthError(message, response);\n case 'transient':\n return new TransientError(message, response);\n case 'upstream_bug':\n return new UpstreamBugError(message, response);\n case 'client_bug':\n return new ClientBugError(message, response);\n }\n}\n","import { HttpClientError, RateLimitError, TransientError } from './errors';\n\nexport interface RetryPolicy {\n maxAttempts?: number;\n initialDelayMs?: number;\n maxDelayMs?: number;\n retryOn?: (status: number | null, err?: Error) => boolean;\n}\n\nexport const defaultRetryOn = (status: number | null, err?: Error): boolean => {\n if (err instanceof RateLimitError) {\n return true;\n }\n if (err instanceof TransientError) {\n return true;\n }\n if (status === null) {\n return err instanceof Error && !(err instanceof HttpClientError);\n }\n if (status === 408 || status === 429) {\n return true;\n }\n if (status >= 500) {\n return true;\n }\n return false;\n};\n\nexport function backoffDelayMs(\n attempt: number,\n policy: Required<Pick<RetryPolicy, 'initialDelayMs' | 'maxDelayMs'>>,\n): number {\n const base = policy.initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, policy.maxDelayMs);\n}\n\nexport function parseRetryAfter(\n headerValue: string | null,\n now: Date = new Date(),\n): Date | undefined {\n if (!headerValue) {\n return undefined;\n }\n const trimmed = headerValue.trim();\n if (/^\\d+$/.test(trimmed)) {\n return new Date(now.getTime() + Number(trimmed) * 1000);\n }\n const parsed = Date.parse(trimmed);\n if (Number.isNaN(parsed)) {\n return undefined;\n }\n return new Date(parsed);\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.reject(signal.reason ?? new Error('Aborted'));\n }\n return new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal!.reason ?? new Error('Aborted'));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n","export const HTTP_CLIENT_VERSION = '0.0.0';\n\nexport const DEFAULT_USER_AGENT = `rawdash-connector/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n\nexport function connectorUserAgent(connectorId: string): string {\n return `rawdash-connector-${connectorId}/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n}\n","import {\n AuthError,\n ClientBugError,\n HttpClientError,\n RateLimitError,\n TransientError,\n UpstreamBugError,\n errorForStatus,\n} from './errors';\nimport { defaultRetryOn, parseRetryAfter, sleep } from './retry';\nimport type { FetchLike, HttpMethod, HttpRequest, HttpResponse } from './types';\nimport { DEFAULT_USER_AGENT } from './version';\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_ATTEMPTS = 3;\nconst DEFAULT_INITIAL_DELAY_MS = 1000;\nconst DEFAULT_MAX_DELAY_MS = 60_000;\nconst OBSERVER_TIMEOUT_MS = 250;\n\nexport interface RequestObservation {\n url: string;\n method: HttpMethod;\n status: number;\n resource: string;\n requestId: string;\n body: unknown;\n}\n\nexport type RequestObserver = (\n event: RequestObservation,\n) => void | Promise<void>;\n\nexport interface RequestOptions {\n fetch?: FetchLike;\n observer?: RequestObserver;\n resource: string;\n requestId?: string;\n}\n\nasync function notifyObserver(\n observer: RequestObserver,\n event: RequestObservation,\n): Promise<void> {\n let result: void | Promise<void>;\n try {\n result = observer(event);\n } catch (err) {\n console.warn('[connector-shared] request observer threw:', err);\n return;\n }\n if (!(result instanceof Promise)) {\n return;\n }\n const guarded = result.catch((err) => {\n console.warn('[connector-shared] request observer rejected:', err);\n });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, OBSERVER_TIMEOUT_MS);\n });\n try {\n await Promise.race([guarded, timeout]);\n } finally {\n if (timer) {\n clearTimeout(timer);\n }\n }\n}\n\nfunction newRequestId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) {\n return c.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction mergeHeaders(\n defaults: Record<string, string>,\n overrides: Record<string, string> | undefined,\n): Record<string, string> {\n const merged: Record<string, string> = {};\n for (const [k, v] of Object.entries(defaults)) {\n merged[k.toLowerCase()] = v;\n }\n if (overrides) {\n for (const [k, v] of Object.entries(overrides)) {\n merged[k.toLowerCase()] = v;\n }\n }\n return merged;\n}\n\nfunction linkTimeoutSignal(\n parent: AbortSignal | undefined,\n timeoutMs: number,\n): { signal: AbortSignal; cancel: () => void } {\n const controller = new AbortController();\n const onParentAbort = () => {\n controller.abort(parent?.reason);\n };\n if (parent) {\n if (parent.aborted) {\n controller.abort(parent.reason);\n } else {\n parent.addEventListener('abort', onParentAbort, { once: true });\n }\n }\n const timer = setTimeout(() => {\n controller.abort(new Error(`Request timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n return {\n signal: controller.signal,\n cancel: () => {\n clearTimeout(timer);\n if (parent) {\n parent.removeEventListener('abort', onParentAbort);\n }\n },\n };\n}\n\nasync function readBody(res: Response, parseJson: boolean): Promise<unknown> {\n if (res.status === 204 || res.status === 205) {\n return null;\n }\n const contentType = res.headers.get('content-type') ?? '';\n if (parseJson && contentType.includes('application/json')) {\n const text = await res.text();\n if (text.length === 0) {\n return null;\n }\n return JSON.parse(text);\n }\n return res.text();\n}\n\nexport async function request<T = unknown>(\n req: HttpRequest,\n options: RequestOptions,\n): Promise<HttpResponse<T>> {\n const fetchImpl: FetchLike = options.fetch ?? (globalThis.fetch as FetchLike);\n const retry = req.retry ?? {};\n const maxAttempts = retry.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;\n const initialDelayMs = retry.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;\n const maxDelayMs = retry.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;\n const retryOn = retry.retryOn ?? defaultRetryOn;\n const timeoutMs = req.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const parseJson = req.parseJson ?? true;\n\n const headers = mergeHeaders(\n {\n 'User-Agent': DEFAULT_USER_AGENT,\n Accept: 'application/json',\n },\n req.headers,\n );\n\n let lastErr: Error | undefined;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n req.signal?.throwIfAborted();\n\n const { signal, cancel } = linkTimeoutSignal(req.signal, timeoutMs);\n let res: Response;\n try {\n res = await fetchImpl(req.url, {\n method: req.method ?? 'GET',\n headers,\n body: req.body as RequestInit['body'],\n signal,\n });\n } catch (err) {\n cancel();\n if (req.signal?.aborted) {\n throw req.signal.reason ?? err;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n lastErr = error;\n if (attempt < maxAttempts - 1 && retryOn(null, error)) {\n const delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n await sleep(delay, req.signal);\n continue;\n }\n throw new TransientError(error.message);\n }\n cancel();\n\n const body = await readBody(res, parseJson);\n const httpResponse: HttpResponse<T> = {\n status: res.status,\n headers: res.headers,\n body: body as T,\n };\n if (req.rateLimit) {\n const state = req.rateLimit.parse(res.headers);\n if (state) {\n httpResponse.rateLimitState = state;\n }\n }\n\n if (options.observer) {\n await notifyObserver(options.observer, {\n url: req.url,\n method: req.method ?? 'GET',\n status: res.status,\n resource: options.resource,\n requestId: options.requestId ?? newRequestId(),\n body,\n });\n }\n\n if (res.ok) {\n return httpResponse;\n }\n\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\n const message = `HTTP ${res.status} ${res.statusText} for ${req.method ?? 'GET'} ${req.url}`;\n const err = errorForStatus(message, httpResponse, retryAfter);\n\n if (\n attempt < maxAttempts - 1 &&\n retryOn(res.status, err) &&\n !(err instanceof AuthError) &&\n !(err instanceof ClientBugError)\n ) {\n lastErr = err;\n let delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n if (err instanceof RateLimitError && retryAfter) {\n const wait = retryAfter.getTime() - Date.now();\n if (wait > 0) {\n delay = Math.min(wait, maxDelayMs);\n }\n }\n await sleep(delay, req.signal);\n continue;\n }\n\n throw err;\n }\n\n throw lastErr ?? new UpstreamBugError('Exhausted retry attempts');\n}\n\nfunction computeDelay(\n attempt: number,\n initialDelayMs: number,\n maxDelayMs: number,\n): number {\n const base = initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, maxDelayMs);\n}\n\nexport { HttpClientError };\n","export interface RateLimitState {\n remaining: number;\n resetAt: Date;\n}\n\nexport interface RateLimitPolicy {\n parse(headers: Headers): RateLimitState | null;\n}\n\nexport interface StandardRateLimitPolicyConfig {\n remainingHeader: string;\n resetHeader: string;\n resetUnit: 's' | 'ms';\n resetFallbackMs?: number;\n}\n\nexport function standardRateLimitPolicy(\n config: StandardRateLimitPolicyConfig,\n): RateLimitPolicy {\n const { remainingHeader, resetHeader, resetUnit, resetFallbackMs } = config;\n const multiplier = resetUnit === 's' ? 1000 : 1;\n return {\n parse(h) {\n const remainingRaw = h.get(remainingHeader);\n if (remainingRaw === null || remainingRaw.trim() === '') {\n return null;\n }\n const remaining = Number(remainingRaw);\n if (!Number.isFinite(remaining)) {\n return null;\n }\n const resetRaw = h.get(resetHeader);\n if (resetRaw === null) {\n if (resetFallbackMs === undefined) {\n return null;\n }\n return {\n remaining,\n resetAt: new Date(Date.now() + resetFallbackMs),\n };\n }\n if (resetRaw.trim() === '') {\n return null;\n }\n const reset = Number(resetRaw);\n if (!Number.isFinite(reset) || reset < 0) {\n return null;\n }\n const resetMs = reset * multiplier;\n if (!Number.isFinite(resetMs)) {\n return null;\n }\n return { remaining, resetAt: new Date(resetMs) };\n },\n };\n}\n","export async function mapWithConcurrency<T, R>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n if (items.length === 0) {\n return results;\n }\n const normalized = Number.isFinite(concurrency) ? Math.floor(concurrency) : 1;\n const limit = Math.max(1, Math.min(normalized, items.length));\n let next = 0;\n let failed = false;\n\n async function worker(): Promise<void> {\n while (!failed) {\n const i = next++;\n if (i >= items.length) {\n return;\n }\n try {\n results[i] = await fn(items[i]!, i);\n } catch (err) {\n failed = true;\n throw err;\n }\n }\n }\n\n const workers: Promise<void>[] = [];\n for (let w = 0; w < limit; w++) {\n workers.push(worker());\n }\n await Promise.all(workers);\n return results;\n}\n","export interface SanitizeAllowedUrlOptions {\n url: string | null;\n host: string;\n pathname: string;\n protocol?: 'https:' | 'http:';\n}\n\nexport function sanitizeAllowedUrl(\n options: SanitizeAllowedUrlOptions,\n): string | null {\n const { url, host, pathname, protocol = 'https:' } = options;\n if (url === null) {\n return null;\n }\n try {\n const u = new URL(url);\n if (u.protocol !== protocol || u.host !== host || u.pathname !== pathname) {\n return null;\n }\n return u.toString();\n } catch {\n return null;\n }\n}\n","export type EpochUnit = 'ms' | 's' | 'iso';\n\nexport function parseEpoch(\n value: number | string | null | undefined,\n unit: EpochUnit,\n): number | null {\n if (value === null || value === undefined) {\n return null;\n }\n if (unit === 'iso') {\n if (typeof value !== 'string') {\n return null;\n }\n const ms = new Date(value).getTime();\n return Number.isFinite(ms) ? ms : null;\n }\n if (typeof value === 'string' && value.trim() === '') {\n return null;\n }\n const n = typeof value === 'number' ? value : Number(value);\n if (!Number.isFinite(n)) {\n return null;\n }\n const result = unit === 's' ? n * 1000 : n;\n return Number.isFinite(result) ? result : null;\n}\n","import { request } from './request';\nimport type { HttpRequest } from './types';\n\nexport function parseLinkHeader(header: string | null): Record<string, string> {\n if (!header) {\n return {};\n }\n const result: Record<string, string> = {};\n for (const part of header.split(',')) {\n const match = part.match(/<([^>]+)>\\s*;\\s*rel=\"([^\"]+)\"/);\n if (match) {\n result[match[2]!] = match[1]!;\n }\n }\n return result;\n}\n\nexport async function* paginateLink<T>(\n initial: HttpRequest,\n parse: (body: unknown) => T[],\n options: { resource: string },\n): AsyncIterable<T> {\n let next: string | null = initial.url;\n while (next) {\n const res: Awaited<ReturnType<typeof request>> = await request(\n {\n ...initial,\n url: next,\n },\n { resource: options.resource },\n );\n for (const item of parse(res.body)) {\n yield item;\n }\n const links = parseLinkHeader(res.headers.get('link'));\n next = links['next'] ?? null;\n }\n}\n\nexport async function* paginateCursor<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; nextCursor: string | null },\n buildNext: (req: HttpRequest, cursor: string) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let req: HttpRequest = initial;\n while (true) {\n const res = await request(req, { resource: options.resource });\n const { items, nextCursor } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!nextCursor) {\n return;\n }\n req = buildNext(req, nextCursor);\n }\n}\n\nexport async function* paginatePage<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; hasMore: boolean },\n buildPage: (req: HttpRequest, page: number) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let page = 1;\n while (true) {\n const req = page === 1 ? initial : buildPage(initial, page);\n const res = await request(req, { resource: options.resource });\n const { items, hasMore } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!hasMore || items.length === 0) {\n return;\n }\n page++;\n }\n}\n","export type LogFields = Record<string, unknown>;\n\nexport interface ConnectorLogger {\n info(event: string, fields?: LogFields): void;\n warn(event: string, fields?: LogFields): void;\n}\n\nexport interface ConnectorLoggerOptions {\n scope: string;\n}\n\nconst MAX_VALUE_LEN = 120;\n\nfunction truncate(s: string, max = MAX_VALUE_LEN): string {\n if (s.length <= max) {\n return s;\n }\n return `${s.slice(0, max - 1)}…`;\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return '';\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (typeof value === 'string') {\n const t = truncate(value);\n if (/[\\s\"=]/.test(t)) {\n return JSON.stringify(t);\n }\n return t;\n }\n if (typeof value === 'bigint') {\n return value.toString();\n }\n let json: string | undefined;\n try {\n json = JSON.stringify(value);\n } catch {\n json = undefined;\n }\n return truncate(json ?? String(value));\n}\n\nexport function formatLogFields(fields?: LogFields): string {\n if (!fields) {\n return '';\n }\n const parts: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (v === undefined) {\n continue;\n }\n parts.push(`${k}=${formatValue(v)}`);\n }\n return parts.length > 0 ? ` ${parts.join(' ')}` : '';\n}\n\nexport function formatLogLine(\n scope: string,\n event: string,\n fields?: LogFields,\n): string {\n return `[${scope}] ${event}${formatLogFields(fields)}`;\n}\n\nexport function createDefaultConnectorLogger(\n opts: ConnectorLoggerOptions,\n): ConnectorLogger {\n return {\n info(event, fields) {\n console.info(formatLogLine(opts.scope, event, fields));\n },\n warn(event, fields) {\n console.warn(formatLogLine(opts.scope, event, fields));\n },\n };\n}\n\nconst NOOP_LOGGER: ConnectorLogger = {\n info() {},\n warn() {},\n};\n\nexport function noopConnectorLogger(): ConnectorLogger {\n return NOOP_LOGGER;\n}\n","import {\n type HttpResponse,\n connectorUserAgent,\n parseLinkHeader,\n sanitizeAllowedUrl,\n standardRateLimitPolicy,\n} from '@rawdash/connector-shared';\nimport {\n BaseConnector,\n type ChunkedSyncCursor,\n type ConnectorContext,\n type ConnectorDoc,\n type CredentialsSchema,\n type FetchPageResult,\n type StorageHandle,\n type SyncOptions,\n type SyncResult,\n defineConfigFields,\n defineConnectorDoc,\n defineResources,\n makeChunkedCursorGuard,\n paginateChunked,\n resolveBackfillCutoff,\n schemasFromResources,\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 const doc: ConnectorDoc = defineConnectorDoc({\n displayName: 'GitHub',\n category: 'engineering',\n brandColor: '#181717',\n tagline:\n 'Sync pull requests, issues, deployments, releases, CI runs, and contributor activity from a GitHub repository.',\n vendor: {\n name: 'GitHub',\n apiDocs: 'https://docs.github.com/rest',\n website: 'https://github.com',\n },\n auth: {\n summary:\n 'A personal access token is optional for public repositories but required for private repos and to avoid the low unauthenticated rate limit.',\n setup: [\n 'Open GitHub → Settings → Developer settings → Personal access tokens.',\n 'Generate a token with the `repo` scope (read access is sufficient).',\n 'Store it as a secret and reference it from the connector config as `token: secret(\"GITHUB_TOKEN\")`.',\n ],\n },\n rateLimit:\n 'Unauthenticated requests share GitHub’s low 60 requests/hour limit; an authenticated token raises it to 5,000 requests/hour.',\n limitations: [\n 'The GitHub REST API can return the same item more than once within a sync (cursor pagination overlapping a mutating collection, retried requests, or an item surfaced via multiple endpoints). Each resource dedupes by stable id before writing, keeping the last copy seen.',\n 'Public repositories without a token are subject to GitHub’s low unauthenticated rate limit.',\n ],\n});\n\nexport interface GitHubSettings {\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\nconst githubRateLimit = standardRateLimitPolicy({\n remainingHeader: 'x-ratelimit-remaining',\n resetHeader: 'x-ratelimit-reset',\n resetUnit: 's',\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\nconst PHASE_RESOURCES: Record<GitHubSyncPhase, readonly string[]> = {\n repo_stats: ['repo'],\n workflow_runs: ['workflow_run'],\n pull_requests: ['pull_request'],\n issues: ['issue'],\n deployments: ['deployment'],\n releases: ['release'],\n contributors: ['contributor'],\n};\n\nfunction selectPhases(\n allowlist: ReadonlySet<string> | undefined,\n): readonly GitHubSyncPhase[] {\n if (allowlist === undefined) {\n return PHASE_ORDER;\n }\n return PHASE_ORDER.filter((phase) =>\n PHASE_RESOURCES[phase].some((r) => allowlist.has(r)),\n );\n}\n\ntype GitHubSyncCursor = ChunkedSyncCursor<GitHubSyncPhase, string>;\n\ninterface PRPageItems {\n prs: GitHubPR[];\n reviewsByPR: Map<number, GitHubReview[]>;\n}\n\ninterface DeploymentPageItems {\n deployments: GitHubDeployment[];\n latestStatusById: Map<number, GitHubDeploymentStatus | null>;\n}\n\nconst CONTRIBUTORS_SKIPPED = Symbol('contributors-skipped');\n\nfunction dedupeByKey<T>(\n items: T[],\n keyFn: (item: T) => string,\n resource: string,\n): T[] {\n if (items.length < 2) {\n return items;\n }\n const seen = new Map<string, T>();\n let duplicates = 0;\n for (const item of items) {\n const key = keyFn(item);\n if (seen.has(key)) {\n duplicates++;\n }\n seen.set(key, item);\n }\n if (duplicates > 0) {\n console.warn(\n `[github-actions] ${resource}: dropped ${duplicates} duplicate id(s) — keeping latest copy of each`,\n );\n }\n return Array.from(seen.values());\n}\n\nconst isGitHubSyncCursor = makeChunkedCursorGuard(PHASE_ORDER);\n\nconst workflowRunsResponseSchema = z.object({\n total_count: z.number().int().optional(),\n workflow_runs: z.array(\n z.object({\n id: z.number().int(),\n name: z.string(),\n conclusion: z.string().nullable(),\n status: z.string(),\n head_branch: z.string().nullable(),\n actor: z.object({ login: z.string().min(1) }).nullable(),\n created_at: z.iso.datetime(),\n updated_at: z.iso.datetime(),\n run_attempt: z.number().int(),\n artifacts_url: z.string().optional(),\n cancel_url: z.string().optional(),\n check_suite_id: z.number().int().optional(),\n check_suite_node_id: z.string().optional(),\n check_suite_url: z.string().optional(),\n display_title: z.string().optional(),\n event: z.string().optional(),\n head_commit: z.unknown().optional(),\n head_repository: z.unknown().optional(),\n head_sha: z.string().optional(),\n html_url: z.string().optional(),\n jobs_url: z.string().optional(),\n logs_url: z.string().optional(),\n node_id: z.string().optional(),\n path: z.string().optional(),\n previous_attempt_url: z.string().nullable().optional(),\n pull_requests: z.array(z.unknown()).optional(),\n referenced_workflows: z.array(z.unknown()).optional(),\n repository: z.unknown().optional(),\n rerun_url: z.string().optional(),\n run_number: z.number().int().optional(),\n run_started_at: z.iso.datetime().optional(),\n triggering_actor: z.object({ login: z.string().min(1) }).optional(),\n url: z.string().optional(),\n workflow_id: z.number().int().optional(),\n workflow_url: z.string().optional(),\n }),\n ),\n});\n\nconst pullRequestsSchema = z.array(\n z.object({\n number: z.number().int(),\n title: z.string(),\n state: z.string(),\n draft: z.boolean(),\n user: z.object({\n login: z.string().min(1),\n avatar_url: z.string().optional(),\n events_url: z.string().optional(),\n followers_url: z.string().optional(),\n following_url: z.string().optional(),\n gists_url: z.string().optional(),\n gravatar_id: z.string().nullable().optional(),\n html_url: z.string().optional(),\n id: z.number().int().optional(),\n node_id: z.string().optional(),\n organizations_url: z.string().optional(),\n received_events_url: z.string().optional(),\n repos_url: z.string().optional(),\n site_admin: z.boolean().optional(),\n starred_url: z.string().optional(),\n subscriptions_url: z.string().optional(),\n type: z.string().optional(),\n url: z.string().optional(),\n user_view_type: z.string().optional(),\n }),\n created_at: z.iso.datetime(),\n updated_at: z.iso.datetime(),\n _links: z.unknown().optional(),\n active_lock_reason: z.string().nullable().optional(),\n assignee: z.unknown().optional(),\n assignees: z.unknown().optional(),\n author_association: z.string().optional(),\n auto_merge: z.unknown().optional(),\n base: z.unknown().optional(),\n body: z.string().nullable().optional(),\n closed_at: z.string().nullable().optional(),\n comments_url: z.string().optional(),\n commits_url: z.string().optional(),\n diff_url: z.string().optional(),\n head: z.unknown().optional(),\n html_url: z.string().optional(),\n id: z.number().int().optional(),\n issue_url: z.string().optional(),\n labels: z.unknown().optional(),\n locked: z.boolean().optional(),\n merge_commit_sha: z.string().nullable().optional(),\n merged_at: z.string().nullable().optional(),\n milestone: z.unknown().optional(),\n node_id: z.string().optional(),\n patch_url: z.string().optional(),\n requested_reviewers: z.unknown().optional(),\n requested_teams: z.unknown().optional(),\n review_comment_url: z.string().optional(),\n review_comments_url: z.string().optional(),\n statuses_url: z.string().optional(),\n url: z.string().optional(),\n }),\n);\n\nconst reviewsSchema = z.array(\n z.object({\n user: z.object({ login: z.string().min(1) }).nullable(),\n state: z.string(),\n submitted_at: z.iso.datetime(),\n }),\n);\n\nconst issuesSchema = z.array(\n z.object({\n number: z.number().int(),\n title: z.string(),\n state: z.string(),\n labels: z.array(\n z.object({\n name: z.string(),\n id: z.number().int().optional(),\n node_id: z.string().optional(),\n url: z.string().optional(),\n color: z.string().optional(),\n default: z.boolean().optional(),\n description: z.string().nullable().optional(),\n }),\n ),\n assignees: z.array(z.object({ login: z.string().min(1) })),\n user: z.object({ login: z.string().min(1) }).catchall(z.unknown()),\n created_at: z.iso.datetime(),\n updated_at: z.iso.datetime(),\n closed_at: z.iso.datetime().nullable().optional(),\n pull_request: z.unknown().optional(),\n active_lock_reason: z.unknown().optional(),\n assignee: z.unknown().optional(),\n author_association: z.string().optional(),\n body: z.string().nullable().optional(),\n closed_by: z.unknown().optional(),\n comments: z.number().int().optional(),\n comments_url: z.string().optional(),\n draft: z.boolean().optional(),\n events_url: z.string().optional(),\n html_url: z.string().optional(),\n id: z.number().int().optional(),\n issue_field_values: z.unknown().optional(),\n labels_url: z.string().optional(),\n locked: z.boolean().optional(),\n milestone: z.unknown().optional(),\n node_id: z.string().optional(),\n performed_via_github_app: z.unknown().optional(),\n reactions: z.unknown().optional(),\n repository_url: z.string().optional(),\n state_reason: z.unknown().optional(),\n timeline_url: z.string().optional(),\n type: z.unknown().optional(),\n url: z.string().optional(),\n }),\n);\n\nconst deploymentsSchema = z.array(\n z.object({\n id: z.number().int(),\n environment: z.string(),\n ref: z.string(),\n sha: z.string(),\n creator: z.object({ login: z.string().min(1) }).nullable(),\n created_at: z.iso.datetime(),\n }),\n);\n\nconst deploymentStatusesSchema = z.array(\n z.object({\n state: z.string(),\n updated_at: z.iso.datetime(),\n }),\n);\n\nconst releasesSchema = z.array(\n z.object({\n id: z.number().int(),\n tag_name: z.string(),\n name: z.string().nullable(),\n draft: z.boolean(),\n prerelease: z.boolean(),\n created_at: z.iso.datetime(),\n published_at: z.iso.datetime().nullable(),\n author: z.object({\n login: z.string().min(1),\n id: z.number().int().optional(),\n node_id: z.string().optional(),\n avatar_url: z.string().optional(),\n gravatar_id: z.string().nullable().optional(),\n url: z.string().optional(),\n html_url: z.string().optional(),\n followers_url: z.string().optional(),\n following_url: z.string().optional(),\n gists_url: z.string().optional(),\n starred_url: z.string().optional(),\n subscriptions_url: z.string().optional(),\n organizations_url: z.string().optional(),\n repos_url: z.string().optional(),\n events_url: z.string().optional(),\n received_events_url: z.string().optional(),\n type: z.string().optional(),\n site_admin: z.boolean().optional(),\n user_view_type: z.string().optional(),\n }),\n node_id: z.string().optional(),\n url: z.string().optional(),\n html_url: z.string().optional(),\n assets_url: z.string().optional(),\n upload_url: z.string().optional(),\n tarball_url: z.string().nullable().optional(),\n zipball_url: z.string().nullable().optional(),\n target_commitish: z.string().optional(),\n body: z.string().nullable().optional(),\n immutable: z.boolean().optional(),\n mentions_count: z.number().int().optional(),\n updated_at: z.iso.datetime().optional(),\n assets: z.array(z.unknown()).optional(),\n }),\n);\n\nconst contributorsSchema = z.array(\n z.object({\n total: z.number().int(),\n weeks: z.array(\n z.object({\n w: z.number().int(),\n a: z.number().int(),\n d: z.number().int(),\n c: z.number().int(),\n }),\n ),\n author: z.object({ login: z.string().min(1) }),\n }),\n);\n\nconst repoStatsSchema = z.object({\n stargazers_count: z.number().int(),\n forks_count: z.number().int(),\n subscribers_count: z.number().int(),\n});\n\nexport const githubResources = defineResources({\n repo: {\n shape: 'entity',\n description:\n 'Top-level repository stats (stars, forks, and watchers) as a single entity.',\n endpoint: 'GET /repos/{owner}/{repo}',\n responses: { repo: repoStatsSchema },\n },\n workflow_run: {\n shape: 'event',\n description: 'GitHub Actions CI pipeline executions.',\n endpoint: 'GET /repos/{owner}/{repo}/actions/runs',\n responses: { workflow_runs: workflowRunsResponseSchema },\n },\n pull_request: {\n shape: 'entity',\n description:\n 'Open and closed pull requests, including draft state, author, and review state.',\n endpoint: 'GET /repos/{owner}/{repo}/pulls',\n notes:\n 'Review state is folded in from GET /repos/{owner}/{repo}/pulls/{number}/reviews per PR.',\n responses: {\n pull_requests: pullRequestsSchema,\n pull_request_reviews: reviewsSchema,\n },\n },\n issue: {\n shape: 'entity',\n description:\n 'Open and closed issues with labels, assignees, and author (pull requests excluded).',\n endpoint: 'GET /repos/{owner}/{repo}/issues',\n responses: { issues: issuesSchema },\n },\n deployment: {\n shape: 'entity',\n description:\n 'Deployments with their latest status, keyed by environment and ref.',\n endpoint: 'GET /repos/{owner}/{repo}/deployments',\n notes:\n 'The latest status is folded in from GET /repos/{owner}/{repo}/deployments/{id}/statuses.',\n responses: {\n deployments: deploymentsSchema,\n deployment_statuses: deploymentStatusesSchema,\n },\n },\n release: {\n shape: 'entity',\n description: 'Published, draft, and prerelease GitHub releases.',\n endpoint: 'GET /repos/{owner}/{repo}/releases',\n responses: { releases: releasesSchema },\n },\n contributor: {\n shape: 'entity',\n description:\n 'Per-author commit activity (commits, additions, deletions) for the repository.',\n endpoint: 'GET /repos/{owner}/{repo}/stats/contributors',\n responses: { contributors: contributorsSchema },\n },\n});\n\nexport const id = 'github-actions';\n\nexport class GitHubConnector extends BaseConnector<\n GitHubSettings,\n GitHubCredentials\n> {\n static readonly id = id;\n\n static readonly resources = githubResources;\n\n static readonly schemas = schemasFromResources(githubResources);\n\n static create(input: unknown, ctx?: ConnectorContext): GitHubConnector {\n const parsed = configFields.parse(input);\n return new GitHubConnector(\n { owner: parsed.owner, repo: parsed.repo },\n { token: parsed.token },\n ctx,\n );\n }\n\n readonly id = id;\n\n override readonly credentials = githubCredentials;\n\n private seenWorkflowRunIds = new Set<string>();\n\n private preservedDeploymentStatus = new Map<string, string>();\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': connectorUserAgent('github'),\n };\n if (this.creds.token) {\n headers['Authorization'] = `Bearer ${this.creds.token}`;\n }\n return headers;\n }\n\n private fetch<T>(\n url: string,\n resource: string,\n signal: AbortSignal | undefined,\n ): Promise<HttpResponse<T>> {\n return this.get<T>(url, {\n resource,\n headers: this.buildHeaders(),\n signal,\n rateLimit: githubRateLimit,\n });\n }\n\n private allowedPageBasePath(phase: GitHubSyncPhase): string | null {\n const { owner, repo } = this.settings;\n switch (phase) {\n case 'workflow_runs':\n return `/repos/${owner}/${repo}/actions/runs`;\n case 'pull_requests':\n return `/repos/${owner}/${repo}/pulls`;\n case 'issues':\n return `/repos/${owner}/${repo}/issues`;\n case 'deployments':\n return `/repos/${owner}/${repo}/deployments`;\n case 'releases':\n return `/repos/${owner}/${repo}/releases`;\n case 'repo_stats':\n case 'contributors':\n return null;\n }\n }\n\n private sanitizePageUrl(\n phase: GitHubSyncPhase,\n pageUrl: string | null,\n ): string | null {\n const allowedPath = this.allowedPageBasePath(phase);\n if (allowedPath === null) {\n return null;\n }\n const canonical = sanitizeAllowedUrl({\n url: pageUrl,\n host: 'api.github.com',\n pathname: allowedPath,\n });\n if (canonical !== null || pageUrl === null) {\n return canonical;\n }\n try {\n const u = new URL(pageUrl);\n const resourceSuffix = allowedPath.replace(/^\\/repos\\/[^/]+\\/[^/]+/, '');\n const escapedSuffix = resourceSuffix.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n '\\\\$&',\n );\n const numericPath = new RegExp(`^/repositories/\\\\d+${escapedSuffix}$`);\n if (\n u.protocol === 'https:' &&\n u.host === 'api.github.com' &&\n numericPath.test(u.pathname)\n ) {\n return u.toString();\n }\n } catch {\n return null;\n }\n return null;\n }\n\n private isResourceAllowed(options: SyncOptions, resource: string): boolean {\n if (!options.resources) {\n return true;\n }\n return options.resources.has(resource);\n }\n\n private resolveCursor(cursor: unknown): GitHubSyncCursor | undefined {\n if (!isGitHubSyncCursor(cursor)) {\n return undefined;\n }\n return {\n phase: cursor.phase,\n page: this.sanitizePageUrl(cursor.phase, cursor.page),\n };\n }\n\n private async fetchRepoStats(\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n const res = await this.fetch<GitHubRepo>(\n `https://api.github.com/repos/${owner}/${repo}`,\n 'repo',\n signal,\n );\n return { items: [res.body], next: null };\n }\n\n private async fetchWorkflowRunsLatest(\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n const res = await this.fetch<GitHubRunsResponse>(\n `https://api.github.com/repos/${owner}/${repo}/actions/runs?per_page=1`,\n 'workflow_runs',\n signal,\n );\n const run = res.body.workflow_runs[0];\n return { items: run ? [run] : [], next: null };\n }\n\n private async fetchWorkflowRunsFull(\n options: SyncOptions,\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n const url =\n page ??\n `https://api.github.com/repos/${owner}/${repo}/actions/runs?per_page=100`;\n const res = await this.fetch<GitHubRunsResponse>(\n url,\n 'workflow_runs',\n signal,\n );\n const nextLink = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n const runs = res.body.workflow_runs;\n const cutoff = resolveBackfillCutoff(options, 'workflow_run', Date.now());\n\n const filtered = runs.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\n const lastRun = runs.at(-1);\n const cutoffReached =\n cutoff !== null &&\n lastRun !== undefined &&\n new Date(lastRun.created_at).getTime() < cutoff &&\n new Date(lastRun.updated_at).getTime() < cutoff;\n\n return {\n items: filtered,\n next: cutoffReached ? null : nextLink,\n };\n }\n\n private async fetchPullRequests(\n options: SyncOptions,\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n const url =\n page ??\n `https://api.github.com/repos/${owner}/${repo}/pulls?state=all&sort=updated&direction=desc&per_page=100`;\n const res = await this.fetch<GitHubPR[]>(url, 'pull_requests', signal);\n const nextLink = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n const prs = res.body;\n const cutoff = resolveBackfillCutoff(options, 'pull_request', Date.now());\n const filteredPrs =\n cutoff !== null\n ? prs.filter((pr) => new Date(pr.updated_at).getTime() >= cutoff)\n : prs;\n const lastPr = prs.at(-1);\n const cutoffReached =\n cutoff !== null &&\n lastPr !== undefined &&\n new Date(lastPr.updated_at).getTime() < cutoff;\n\n const reviewsByPR = new Map<number, GitHubReview[]>();\n if (this.isResourceAllowed(options, 'pull_request_reviews')) {\n for (const pr of filteredPrs) {\n signal?.throwIfAborted();\n const reviews = await this.fetch<GitHubReview[]>(\n `https://api.github.com/repos/${owner}/${repo}/pulls/${pr.number}/reviews`,\n 'pull_request_reviews',\n signal,\n );\n reviewsByPR.set(pr.number, reviews.body);\n }\n }\n\n const items: PRPageItems[] = [{ prs: filteredPrs, reviewsByPR }];\n return { items, next: cutoffReached ? null : nextLink };\n }\n\n private async fetchIssues(\n options: SyncOptions,\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n let url: string;\n if (page) {\n url = page;\n } else {\n const u = new URL(`https://api.github.com/repos/${owner}/${repo}/issues`);\n u.searchParams.set('state', 'all');\n u.searchParams.set('per_page', '100');\n const cutoff = resolveBackfillCutoff(options, 'issue', Date.now());\n if (cutoff !== null) {\n u.searchParams.set('since', new Date(cutoff).toISOString());\n }\n url = u.toString();\n }\n const res = await this.fetch<GitHubIssue[]>(url, 'issues', signal);\n const nextLink = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n return { items: res.body, next: nextLink };\n }\n\n private async fetchDeployments(\n options: SyncOptions,\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n const url =\n page ??\n `https://api.github.com/repos/${owner}/${repo}/deployments?per_page=100`;\n const res = await this.fetch<GitHubDeployment[]>(\n url,\n 'deployments',\n signal,\n );\n const nextLink = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n const deployments = res.body;\n const cutoff = resolveBackfillCutoff(options, 'deployment', Date.now());\n const filteredDeployments =\n cutoff !== null\n ? deployments.filter((d) => new Date(d.created_at).getTime() >= cutoff)\n : deployments;\n const lastDeployment = deployments.at(-1);\n const cutoffReached =\n cutoff !== null &&\n lastDeployment !== undefined &&\n new Date(lastDeployment.created_at).getTime() < cutoff;\n\n const latestStatusById = new Map<number, GitHubDeploymentStatus | null>();\n if (this.isResourceAllowed(options, 'deployment_statuses')) {\n for (const deployment of filteredDeployments) {\n signal?.throwIfAborted();\n const statusRes = await this.fetch<GitHubDeploymentStatus[]>(\n `https://api.github.com/repos/${owner}/${repo}/deployments/${deployment.id}/statuses?per_page=1`,\n 'deployment_statuses',\n signal,\n );\n latestStatusById.set(deployment.id, statusRes.body[0] ?? null);\n }\n }\n\n const items: DeploymentPageItems[] = [\n { deployments: filteredDeployments, latestStatusById },\n ];\n return { items, next: cutoffReached ? null : nextLink };\n }\n\n private async fetchReleases(\n options: SyncOptions,\n page: string | null,\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n const url =\n page ??\n `https://api.github.com/repos/${owner}/${repo}/releases?per_page=100`;\n const res = await this.fetch<GitHubRelease[]>(url, 'releases', signal);\n const nextLink = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n const releases = res.body;\n const cutoff = resolveBackfillCutoff(options, 'release', Date.now());\n const filtered =\n cutoff !== null\n ? releases.filter((r) => {\n const ts = new Date(r.published_at ?? r.created_at).getTime();\n return ts >= cutoff;\n })\n : releases;\n const lastRelease = releases.at(-1);\n const cutoffReached =\n cutoff !== null &&\n lastRelease !== undefined &&\n new Date(lastRelease.published_at ?? lastRelease.created_at).getTime() <\n cutoff;\n return { items: filtered, next: cutoffReached ? null : nextLink };\n }\n\n private async fetchContributors(\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n const contributors = await this.withRetry<GitHubContributorStats[]>(\n async (sig) => {\n const res = await this.fetch<GitHubContributorStats[] | null>(\n `https://api.github.com/repos/${owner}/${repo}/stats/contributors`,\n '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) {\n console.warn(\n '[github-actions] Stats endpoint never became ready — skipping contributor sync and keeping previous data.',\n );\n return { items: [CONTRIBUTORS_SKIPPED], next: null };\n }\n return { items: contributors, next: null };\n }\n\n private async writeRepoStats(\n storage: StorageHandle,\n items: unknown[],\n ): Promise<void> {\n const repoBody = items[0] as GitHubRepo | undefined;\n if (!repoBody) {\n return;\n }\n const { owner, repo } = this.settings;\n await storage.entities(\n [\n {\n type: 'repo',\n id: `${owner}/${repo}`,\n attributes: {\n stars: repoBody.stargazers_count,\n forks: repoBody.forks_count,\n watchers: repoBody.subscribers_count,\n },\n updated_at: Date.now(),\n },\n ],\n { types: ['repo'] },\n );\n }\n\n private async writeWorkflowRunsLatest(\n storage: StorageHandle,\n items: unknown[],\n ): Promise<void> {\n const run = items[0] as\n | GitHubRunsResponse['workflow_runs'][number]\n | undefined;\n if (!run) {\n return;\n }\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\n private async writeWorkflowRunsFull(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n ): Promise<void> {\n if (page === null) {\n await storage.events([], { names: ['workflow_run'] });\n this.seenWorkflowRunIds.clear();\n }\n const withinPage = dedupeByKey(\n items as GitHubRunsResponse['workflow_runs'],\n (run) => String(run.id),\n 'workflow_runs',\n );\n let crossPageDuplicates = 0;\n const runs: GitHubRunsResponse['workflow_runs'] = [];\n for (const run of withinPage) {\n const key = String(run.id);\n if (this.seenWorkflowRunIds.has(key)) {\n crossPageDuplicates++;\n continue;\n }\n this.seenWorkflowRunIds.add(key);\n runs.push(run);\n }\n if (crossPageDuplicates > 0) {\n console.warn(\n `[github-actions] workflow_runs: dropped ${crossPageDuplicates} duplicate id(s) seen on an earlier page`,\n );\n }\n for (const run of runs) {\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 }\n\n private async writePullRequests(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n options: SyncOptions,\n ): Promise<void> {\n const reviewsAllowed = this.isResourceAllowed(\n options,\n 'pull_request_reviews',\n );\n if (page === null) {\n await storage.entities([], { types: ['pull_request'] });\n if (reviewsAllowed) {\n await storage.edges([], { kinds: ['reviewed_by'] });\n }\n }\n const pageItems = items as PRPageItems[];\n for (const { prs: rawPrs, reviewsByPR } of pageItems) {\n const prs = dedupeByKey(\n rawPrs,\n (pr) => String(pr.number),\n 'pull_requests',\n );\n for (const pr of prs) {\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 if (!reviewsAllowed) {\n continue;\n }\n for (const pr of prs) {\n const reviews = reviewsByPR.get(pr.number) ?? [];\n for (const review of reviews) {\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 }\n\n private async writeIssues(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n ): Promise<void> {\n if (page === null) {\n await storage.entities([], { types: ['issue'] });\n }\n const issues = dedupeByKey(\n (items as GitHubIssue[]).filter((i) => i.pull_request === undefined),\n (issue) => String(issue.number),\n 'issues',\n );\n for (const issue of issues) {\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\n private async writeDeployments(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n options: SyncOptions,\n ): Promise<void> {\n const statusesAllowed = this.isResourceAllowed(\n options,\n 'deployment_statuses',\n );\n if (page === null) {\n if (!statusesAllowed) {\n const existing = await storage.queryEntities({ type: 'deployment' });\n for (const entity of existing) {\n const prev = entity.attributes['latest_status'];\n if (typeof prev === 'string') {\n this.preservedDeploymentStatus.set(entity.id, prev);\n }\n }\n }\n await storage.entities([], { types: ['deployment'] });\n }\n const pageItems = items as DeploymentPageItems[];\n for (const { deployments: rawDeployments, latestStatusById } of pageItems) {\n const deployments = dedupeByKey(\n rawDeployments,\n (d) => String(d.id),\n 'deployments',\n );\n for (const deployment of deployments) {\n const createdMs = new Date(deployment.created_at).getTime();\n let latestStatus: string;\n let statusUpdatedMs: number | null = null;\n if (statusesAllowed) {\n const status = latestStatusById.get(deployment.id) ?? null;\n latestStatus = status?.state ?? 'unknown';\n statusUpdatedMs = status?.updated_at\n ? new Date(status.updated_at).getTime()\n : null;\n } else {\n latestStatus =\n this.preservedDeploymentStatus.get(String(deployment.id)) ??\n 'unknown';\n }\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: latestStatus,\n },\n updated_at: Math.max(createdMs, statusUpdatedMs ?? 0),\n });\n }\n }\n }\n\n private async writeReleases(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n ): Promise<void> {\n if (page === null) {\n await storage.entities([], { types: ['release'] });\n }\n const releases = dedupeByKey(\n items as GitHubRelease[],\n (r) => String(r.id),\n 'releases',\n );\n for (const release of releases) {\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\n private async writeContributors(\n storage: StorageHandle,\n items: unknown[],\n ): Promise<void> {\n if (items[0] === CONTRIBUTORS_SKIPPED) {\n return;\n }\n const contributors = dedupeByKey(\n items as GitHubContributorStats[],\n (c) => c.author.login,\n 'contributors',\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 async sync(\n options: SyncOptions,\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<SyncResult> {\n const cursor = this.resolveCursor(options.cursor);\n const phases = selectPhases(options.resources);\n return paginateChunked<GitHubSyncPhase, string>({\n phases,\n cursor,\n signal,\n logger: this.logger,\n fetchPage: async (phase, page, sig) => {\n switch (phase) {\n case 'repo_stats':\n return this.fetchRepoStats(sig);\n case 'workflow_runs':\n return options.mode === 'latest'\n ? this.fetchWorkflowRunsLatest(sig)\n : this.fetchWorkflowRunsFull(options, page, sig);\n case 'pull_requests':\n return this.fetchPullRequests(options, page, sig);\n case 'issues':\n return this.fetchIssues(options, page, sig);\n case 'deployments':\n return this.fetchDeployments(options, page, sig);\n case 'releases':\n return this.fetchReleases(options, page, sig);\n case 'contributors':\n return this.fetchContributors(sig);\n }\n },\n writeBatch: async (phase, items, page) => {\n switch (phase) {\n case 'repo_stats':\n return this.writeRepoStats(storage, items);\n case 'workflow_runs':\n return options.mode === 'latest'\n ? this.writeWorkflowRunsLatest(storage, items)\n : this.writeWorkflowRunsFull(storage, items, page);\n case 'pull_requests':\n return this.writePullRequests(storage, items, page, options);\n case 'issues':\n return this.writeIssues(storage, items, page);\n case 'deployments':\n return this.writeDeployments(storage, items, page, options);\n case 'releases':\n return this.writeReleases(storage, items, page);\n case 'contributors':\n return this.writeContributors(storage, items);\n }\n },\n });\n }\n}\n","import { GitHubConnector } from './github';\n\nexport {\n configFields,\n doc,\n GitHubConnector,\n githubResources as resources,\n id,\n} from './github';\nexport type { GitHubSettings } from './github';\nexport default GitHubConnector;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../connector-shared/src/errors.ts","../../../connector-shared/src/retry.ts","../../../connector-shared/src/version.ts","../../../connector-shared/src/request.ts","../../../connector-shared/src/rate-limit.ts","../../../connector-shared/src/map-concurrent.ts","../../../connector-shared/src/sanitize.ts","../../../connector-shared/src/epoch.ts","../../../connector-shared/src/pagination.ts","../../../connector-shared/src/logger.ts","../src/github.ts","../src/index.ts"],"sourcesContent":["import type { HttpResponse } from './types';\n\nexport type HttpErrorKind =\n | 'transient'\n | 'rate_limit'\n | 'auth'\n | 'upstream_bug'\n | 'client_bug';\n\nexport abstract class HttpClientError extends Error {\n abstract readonly kind: HttpErrorKind;\n readonly response?: HttpResponse;\n\n constructor(message: string, response?: HttpResponse) {\n super(message);\n this.name = new.target.name;\n this.response = response;\n }\n}\n\nexport class TransientError extends HttpClientError {\n readonly kind = 'transient' as const;\n}\n\nexport class RateLimitError extends HttpClientError {\n readonly kind = 'rate_limit' as const;\n readonly retryAfter?: Date;\n\n constructor(message: string, response?: HttpResponse, retryAfter?: Date) {\n super(message, response);\n this.retryAfter = retryAfter;\n }\n}\n\nexport class AuthError extends HttpClientError {\n readonly kind = 'auth' as const;\n}\n\nexport class UpstreamBugError extends HttpClientError {\n readonly kind = 'upstream_bug' as const;\n}\n\nexport class ClientBugError extends HttpClientError {\n readonly kind = 'client_bug' as const;\n}\n\nexport function classifyStatus(status: number): HttpErrorKind {\n if (status === 429) {\n return 'rate_limit';\n }\n if (status === 401 || status === 403) {\n return 'auth';\n }\n if (status === 408) {\n return 'transient';\n }\n if (status >= 500) {\n return 'upstream_bug';\n }\n if (status >= 400) {\n return 'client_bug';\n }\n return 'client_bug';\n}\n\nexport function errorForStatus(\n message: string,\n response: HttpResponse,\n retryAfter?: Date,\n): HttpClientError {\n const kind = classifyStatus(response.status);\n switch (kind) {\n case 'rate_limit':\n return new RateLimitError(message, response, retryAfter);\n case 'auth':\n return new AuthError(message, response);\n case 'transient':\n return new TransientError(message, response);\n case 'upstream_bug':\n return new UpstreamBugError(message, response);\n case 'client_bug':\n return new ClientBugError(message, response);\n }\n}\n","import { HttpClientError, RateLimitError, TransientError } from './errors';\n\nexport interface RetryPolicy {\n maxAttempts?: number;\n initialDelayMs?: number;\n maxDelayMs?: number;\n retryOn?: (status: number | null, err?: Error) => boolean;\n}\n\nexport const defaultRetryOn = (status: number | null, err?: Error): boolean => {\n if (err instanceof RateLimitError) {\n return true;\n }\n if (err instanceof TransientError) {\n return true;\n }\n if (status === null) {\n return err instanceof Error && !(err instanceof HttpClientError);\n }\n if (status === 408 || status === 429) {\n return true;\n }\n if (status >= 500) {\n return true;\n }\n return false;\n};\n\nexport function backoffDelayMs(\n attempt: number,\n policy: Required<Pick<RetryPolicy, 'initialDelayMs' | 'maxDelayMs'>>,\n): number {\n const base = policy.initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, policy.maxDelayMs);\n}\n\nexport function parseRetryAfter(\n headerValue: string | null,\n now: Date = new Date(),\n): Date | undefined {\n if (!headerValue) {\n return undefined;\n }\n const trimmed = headerValue.trim();\n if (/^\\d+$/.test(trimmed)) {\n return new Date(now.getTime() + Number(trimmed) * 1000);\n }\n const parsed = Date.parse(trimmed);\n if (Number.isNaN(parsed)) {\n return undefined;\n }\n return new Date(parsed);\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.reject(signal.reason ?? new Error('Aborted'));\n }\n return new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n clearTimeout(timer);\n reject(signal!.reason ?? new Error('Aborted'));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n","export const HTTP_CLIENT_VERSION = '0.0.0';\n\nexport const DEFAULT_USER_AGENT = `rawdash-connector/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n\nexport function connectorUserAgent(connectorId: string): string {\n return `rawdash-connector-${connectorId}/${HTTP_CLIENT_VERSION} (+https://rawdash.dev)`;\n}\n","import {\n AuthError,\n ClientBugError,\n HttpClientError,\n RateLimitError,\n TransientError,\n UpstreamBugError,\n errorForStatus,\n} from './errors';\nimport { defaultRetryOn, parseRetryAfter, sleep } from './retry';\nimport type { FetchLike, HttpMethod, HttpRequest, HttpResponse } from './types';\nimport { DEFAULT_USER_AGENT } from './version';\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_ATTEMPTS = 3;\nconst DEFAULT_INITIAL_DELAY_MS = 1000;\nconst DEFAULT_MAX_DELAY_MS = 60_000;\nconst OBSERVER_TIMEOUT_MS = 250;\n\nexport interface RequestObservation {\n url: string;\n method: HttpMethod;\n status: number;\n resource: string;\n requestId: string;\n body: unknown;\n}\n\nexport type RequestObserver = (\n event: RequestObservation,\n) => void | Promise<void>;\n\nexport interface RequestOptions {\n fetch?: FetchLike;\n observer?: RequestObserver;\n resource: string;\n requestId?: string;\n}\n\nasync function notifyObserver(\n observer: RequestObserver,\n event: RequestObservation,\n): Promise<void> {\n let result: void | Promise<void>;\n try {\n result = observer(event);\n } catch (err) {\n console.warn('[connector-shared] request observer threw:', err);\n return;\n }\n if (!(result instanceof Promise)) {\n return;\n }\n const guarded = result.catch((err) => {\n console.warn('[connector-shared] request observer rejected:', err);\n });\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, OBSERVER_TIMEOUT_MS);\n });\n try {\n await Promise.race([guarded, timeout]);\n } finally {\n if (timer) {\n clearTimeout(timer);\n }\n }\n}\n\nfunction newRequestId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (c?.randomUUID) {\n return c.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction mergeHeaders(\n defaults: Record<string, string>,\n overrides: Record<string, string> | undefined,\n): Record<string, string> {\n const merged: Record<string, string> = {};\n for (const [k, v] of Object.entries(defaults)) {\n merged[k.toLowerCase()] = v;\n }\n if (overrides) {\n for (const [k, v] of Object.entries(overrides)) {\n merged[k.toLowerCase()] = v;\n }\n }\n return merged;\n}\n\nfunction linkTimeoutSignal(\n parent: AbortSignal | undefined,\n timeoutMs: number,\n): { signal: AbortSignal; cancel: () => void } {\n const controller = new AbortController();\n const onParentAbort = () => {\n controller.abort(parent?.reason);\n };\n if (parent) {\n if (parent.aborted) {\n controller.abort(parent.reason);\n } else {\n parent.addEventListener('abort', onParentAbort, { once: true });\n }\n }\n const timer = setTimeout(() => {\n controller.abort(new Error(`Request timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n return {\n signal: controller.signal,\n cancel: () => {\n clearTimeout(timer);\n if (parent) {\n parent.removeEventListener('abort', onParentAbort);\n }\n },\n };\n}\n\nasync function readBody(res: Response, parseJson: boolean): Promise<unknown> {\n if (res.status === 204 || res.status === 205) {\n return null;\n }\n const contentType = res.headers.get('content-type') ?? '';\n if (parseJson && contentType.includes('application/json')) {\n const text = await res.text();\n if (text.length === 0) {\n return null;\n }\n return JSON.parse(text);\n }\n return res.text();\n}\n\nexport async function request<T = unknown>(\n req: HttpRequest,\n options: RequestOptions,\n): Promise<HttpResponse<T>> {\n const fetchImpl: FetchLike = options.fetch ?? (globalThis.fetch as FetchLike);\n const retry = req.retry ?? {};\n const maxAttempts = retry.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;\n const initialDelayMs = retry.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;\n const maxDelayMs = retry.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;\n const retryOn = retry.retryOn ?? defaultRetryOn;\n const timeoutMs = req.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const parseJson = req.parseJson ?? true;\n\n const headers = mergeHeaders(\n {\n 'User-Agent': DEFAULT_USER_AGENT,\n Accept: 'application/json',\n },\n req.headers,\n );\n\n let lastErr: Error | undefined;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n req.signal?.throwIfAborted();\n\n const { signal, cancel } = linkTimeoutSignal(req.signal, timeoutMs);\n let res: Response;\n try {\n res = await fetchImpl(req.url, {\n method: req.method ?? 'GET',\n headers,\n body: req.body as RequestInit['body'],\n signal,\n });\n } catch (err) {\n cancel();\n if (req.signal?.aborted) {\n throw req.signal.reason ?? err;\n }\n const error = err instanceof Error ? err : new Error(String(err));\n lastErr = error;\n if (attempt < maxAttempts - 1 && retryOn(null, error)) {\n const delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n await sleep(delay, req.signal);\n continue;\n }\n throw new TransientError(error.message);\n }\n cancel();\n\n const body = await readBody(res, parseJson);\n const httpResponse: HttpResponse<T> = {\n status: res.status,\n headers: res.headers,\n body: body as T,\n };\n if (req.rateLimit) {\n const state = req.rateLimit.parse(res.headers);\n if (state) {\n httpResponse.rateLimitState = state;\n }\n }\n\n if (options.observer) {\n await notifyObserver(options.observer, {\n url: req.url,\n method: req.method ?? 'GET',\n status: res.status,\n resource: options.resource,\n requestId: options.requestId ?? newRequestId(),\n body,\n });\n }\n\n if (res.ok) {\n return httpResponse;\n }\n\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\n const message = `HTTP ${res.status} ${res.statusText} for ${req.method ?? 'GET'} ${req.url}`;\n const err = errorForStatus(message, httpResponse, retryAfter);\n\n if (\n attempt < maxAttempts - 1 &&\n retryOn(res.status, err) &&\n !(err instanceof AuthError) &&\n !(err instanceof ClientBugError)\n ) {\n lastErr = err;\n let delay = computeDelay(attempt, initialDelayMs, maxDelayMs);\n if (err instanceof RateLimitError && retryAfter) {\n const wait = retryAfter.getTime() - Date.now();\n if (wait > 0) {\n delay = Math.min(wait, maxDelayMs);\n }\n }\n await sleep(delay, req.signal);\n continue;\n }\n\n throw err;\n }\n\n throw lastErr ?? new UpstreamBugError('Exhausted retry attempts');\n}\n\nfunction computeDelay(\n attempt: number,\n initialDelayMs: number,\n maxDelayMs: number,\n): number {\n const base = initialDelayMs * 2 ** attempt;\n const jitter = base * 0.25 * Math.random();\n return Math.min(base + jitter, maxDelayMs);\n}\n\nexport { HttpClientError };\n","export interface RateLimitState {\n remaining: number;\n resetAt: Date;\n}\n\nexport interface RateLimitPolicy {\n parse(headers: Headers): RateLimitState | null;\n}\n\nexport interface StandardRateLimitPolicyConfig {\n remainingHeader: string;\n resetHeader: string;\n resetUnit: 's' | 'ms';\n resetFallbackMs?: number;\n}\n\nexport function standardRateLimitPolicy(\n config: StandardRateLimitPolicyConfig,\n): RateLimitPolicy {\n const { remainingHeader, resetHeader, resetUnit, resetFallbackMs } = config;\n const multiplier = resetUnit === 's' ? 1000 : 1;\n return {\n parse(h) {\n const remainingRaw = h.get(remainingHeader);\n if (remainingRaw === null || remainingRaw.trim() === '') {\n return null;\n }\n const remaining = Number(remainingRaw);\n if (!Number.isFinite(remaining)) {\n return null;\n }\n const resetRaw = h.get(resetHeader);\n if (resetRaw === null) {\n if (resetFallbackMs === undefined) {\n return null;\n }\n return {\n remaining,\n resetAt: new Date(Date.now() + resetFallbackMs),\n };\n }\n if (resetRaw.trim() === '') {\n return null;\n }\n const reset = Number(resetRaw);\n if (!Number.isFinite(reset) || reset < 0) {\n return null;\n }\n const resetMs = reset * multiplier;\n if (!Number.isFinite(resetMs)) {\n return null;\n }\n return { remaining, resetAt: new Date(resetMs) };\n },\n };\n}\n","export async function mapWithConcurrency<T, R>(\n items: readonly T[],\n concurrency: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n if (items.length === 0) {\n return results;\n }\n const normalized = Number.isFinite(concurrency) ? Math.floor(concurrency) : 1;\n const limit = Math.max(1, Math.min(normalized, items.length));\n let next = 0;\n let failed = false;\n\n async function worker(): Promise<void> {\n while (!failed) {\n const i = next++;\n if (i >= items.length) {\n return;\n }\n try {\n results[i] = await fn(items[i]!, i);\n } catch (err) {\n failed = true;\n throw err;\n }\n }\n }\n\n const workers: Promise<void>[] = [];\n for (let w = 0; w < limit; w++) {\n workers.push(worker());\n }\n await Promise.all(workers);\n return results;\n}\n","export interface SanitizeAllowedUrlOptions {\n url: string | null;\n host: string;\n pathname: string;\n protocol?: 'https:' | 'http:';\n}\n\nexport function sanitizeAllowedUrl(\n options: SanitizeAllowedUrlOptions,\n): string | null {\n const { url, host, pathname, protocol = 'https:' } = options;\n if (url === null) {\n return null;\n }\n try {\n const u = new URL(url);\n if (u.protocol !== protocol || u.host !== host || u.pathname !== pathname) {\n return null;\n }\n return u.toString();\n } catch {\n return null;\n }\n}\n","export type EpochUnit = 'ms' | 's' | 'iso';\n\nexport function parseEpoch(\n value: number | string | null | undefined,\n unit: EpochUnit,\n): number | null {\n if (value === null || value === undefined) {\n return null;\n }\n if (unit === 'iso') {\n if (typeof value !== 'string') {\n return null;\n }\n const ms = new Date(value).getTime();\n return Number.isFinite(ms) ? ms : null;\n }\n if (typeof value === 'string' && value.trim() === '') {\n return null;\n }\n const n = typeof value === 'number' ? value : Number(value);\n if (!Number.isFinite(n)) {\n return null;\n }\n const result = unit === 's' ? n * 1000 : n;\n return Number.isFinite(result) ? result : null;\n}\n","import { request } from './request';\nimport type { HttpRequest } from './types';\n\nexport function parseLinkHeader(header: string | null): Record<string, string> {\n if (!header) {\n return {};\n }\n const result: Record<string, string> = {};\n for (const part of header.split(',')) {\n const match = part.match(/<([^>]+)>\\s*;\\s*rel=\"([^\"]+)\"/);\n if (match) {\n result[match[2]!] = match[1]!;\n }\n }\n return result;\n}\n\nexport async function* paginateLink<T>(\n initial: HttpRequest,\n parse: (body: unknown) => T[],\n options: { resource: string },\n): AsyncIterable<T> {\n let next: string | null = initial.url;\n while (next) {\n const res: Awaited<ReturnType<typeof request>> = await request(\n {\n ...initial,\n url: next,\n },\n { resource: options.resource },\n );\n for (const item of parse(res.body)) {\n yield item;\n }\n const links = parseLinkHeader(res.headers.get('link'));\n next = links['next'] ?? null;\n }\n}\n\nexport async function* paginateCursor<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; nextCursor: string | null },\n buildNext: (req: HttpRequest, cursor: string) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let req: HttpRequest = initial;\n while (true) {\n const res = await request(req, { resource: options.resource });\n const { items, nextCursor } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!nextCursor) {\n return;\n }\n req = buildNext(req, nextCursor);\n }\n}\n\nexport async function* paginatePage<T>(\n initial: HttpRequest,\n parse: (body: unknown) => { items: T[]; hasMore: boolean },\n buildPage: (req: HttpRequest, page: number) => HttpRequest,\n options: { resource: string },\n): AsyncIterable<T> {\n let page = 1;\n while (true) {\n const req = page === 1 ? initial : buildPage(initial, page);\n const res = await request(req, { resource: options.resource });\n const { items, hasMore } = parse(res.body);\n for (const item of items) {\n yield item;\n }\n if (!hasMore || items.length === 0) {\n return;\n }\n page++;\n }\n}\n","export type LogFields = Record<string, unknown>;\n\nexport interface ConnectorLogger {\n info(event: string, fields?: LogFields): void;\n warn(event: string, fields?: LogFields): void;\n}\n\nexport interface ConnectorLoggerOptions {\n scope: string;\n}\n\nconst MAX_VALUE_LEN = 120;\n\nfunction truncate(s: string, max = MAX_VALUE_LEN): string {\n if (s.length <= max) {\n return s;\n }\n return `${s.slice(0, max - 1)}…`;\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (value === undefined) {\n return '';\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n if (typeof value === 'string') {\n const t = truncate(value);\n if (/[\\s\"=]/.test(t)) {\n return JSON.stringify(t);\n }\n return t;\n }\n if (typeof value === 'bigint') {\n return value.toString();\n }\n let json: string | undefined;\n try {\n json = JSON.stringify(value);\n } catch {\n json = undefined;\n }\n return truncate(json ?? String(value));\n}\n\nexport function formatLogFields(fields?: LogFields): string {\n if (!fields) {\n return '';\n }\n const parts: string[] = [];\n for (const [k, v] of Object.entries(fields)) {\n if (v === undefined) {\n continue;\n }\n parts.push(`${k}=${formatValue(v)}`);\n }\n return parts.length > 0 ? ` ${parts.join(' ')}` : '';\n}\n\nexport function formatLogLine(\n scope: string,\n event: string,\n fields?: LogFields,\n): string {\n return `[${scope}] ${event}${formatLogFields(fields)}`;\n}\n\nexport function createDefaultConnectorLogger(\n opts: ConnectorLoggerOptions,\n): ConnectorLogger {\n return {\n info(event, fields) {\n console.info(formatLogLine(opts.scope, event, fields));\n },\n warn(event, fields) {\n console.warn(formatLogLine(opts.scope, event, fields));\n },\n };\n}\n\nconst NOOP_LOGGER: ConnectorLogger = {\n info() {},\n warn() {},\n};\n\nexport function noopConnectorLogger(): ConnectorLogger {\n return NOOP_LOGGER;\n}\n","import {\n type HttpResponse,\n connectorUserAgent,\n parseLinkHeader,\n sanitizeAllowedUrl,\n standardRateLimitPolicy,\n} from '@rawdash/connector-shared';\nimport {\n BaseConnector,\n type ChunkedSyncCursor,\n type ConnectorContext,\n type ConnectorDoc,\n type CredentialsSchema,\n type FetchPageResult,\n type FetchSpec,\n type FilterClause,\n type StorageHandle,\n type SyncOptions,\n type SyncResult,\n defineConfigFields,\n defineConnectorDoc,\n defineResources,\n makeChunkedCursorGuard,\n paginateChunked,\n resolveBackfillCutoff,\n resolveSpecCutoff,\n schemasFromResources,\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 const doc: ConnectorDoc = defineConnectorDoc({\n displayName: 'GitHub',\n category: 'engineering',\n brandColor: '#181717',\n tagline:\n 'Sync pull requests, issues, deployments, releases, CI runs, and contributor activity from a GitHub repository.',\n vendor: {\n name: 'GitHub',\n domain: 'github.com',\n apiDocs: 'https://docs.github.com/rest',\n website: 'https://github.com',\n },\n auth: {\n summary:\n 'A personal access token is optional for public repositories but required for private repos and to avoid the low unauthenticated rate limit.',\n setup: [\n 'Open GitHub → Settings → Developer settings → Personal access tokens.',\n 'Generate a token with the `repo` scope (read access is sufficient).',\n 'Store it as a secret and reference it from the connector config as `token: secret(\"GITHUB_TOKEN\")`.',\n ],\n },\n rateLimit:\n 'Unauthenticated requests share GitHub’s low 60 requests/hour limit; an authenticated token raises it to 5,000 requests/hour.',\n limitations: [\n 'The GitHub REST API can return the same item more than once within a sync (cursor pagination overlapping a mutating collection, retried requests, or an item surfaced via multiple endpoints). Each resource dedupes by stable id before writing, keeping the last copy seen.',\n 'Public repositories without a token are subject to GitHub’s low unauthenticated rate limit.',\n ],\n});\n\nexport interface GitHubSettings {\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\nconst githubRateLimit = standardRateLimitPolicy({\n remainingHeader: 'x-ratelimit-remaining',\n resetHeader: 'x-ratelimit-reset',\n resetUnit: 's',\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\nconst PHASE_RESOURCES: Record<GitHubSyncPhase, readonly string[]> = {\n repo_stats: ['repo'],\n workflow_runs: ['workflow_run'],\n pull_requests: ['pull_request'],\n issues: ['issue'],\n deployments: ['deployment'],\n releases: ['release'],\n contributors: ['contributor'],\n};\n\nconst SINGLE_SPEC_PHASES: ReadonlySet<GitHubSyncPhase> = new Set([\n 'repo_stats',\n 'contributors',\n]);\n\nfunction pushableState(filter: FilterClause[] | undefined): string | null {\n if (!filter) {\n return null;\n }\n for (const clause of filter) {\n if ('field' in clause && clause.field === 'state' && clause.op === 'eq') {\n const { value } = clause;\n if (value === 'open' || value === 'closed') {\n return value;\n }\n }\n }\n return null;\n}\n\nfunction selectPhases(\n allowlist: ReadonlySet<string> | undefined,\n): readonly GitHubSyncPhase[] {\n if (allowlist === undefined) {\n return PHASE_ORDER;\n }\n return PHASE_ORDER.filter((phase) =>\n PHASE_RESOURCES[phase].some((r) => allowlist.has(r)),\n );\n}\n\ntype GitHubSyncCursor = ChunkedSyncCursor<GitHubSyncPhase, string>;\n\ninterface PRPageItems {\n prs: GitHubPR[];\n reviewsByPR: Map<number, GitHubReview[]>;\n}\n\ninterface DeploymentPageItems {\n deployments: GitHubDeployment[];\n latestStatusById: Map<number, GitHubDeploymentStatus | null>;\n}\n\nconst CONTRIBUTORS_SKIPPED = Symbol('contributors-skipped');\n\nfunction dedupeByKey<T>(\n items: T[],\n keyFn: (item: T) => string,\n resource: string,\n): T[] {\n if (items.length < 2) {\n return items;\n }\n const seen = new Map<string, T>();\n let duplicates = 0;\n for (const item of items) {\n const key = keyFn(item);\n if (seen.has(key)) {\n duplicates++;\n }\n seen.set(key, item);\n }\n if (duplicates > 0) {\n console.warn(\n `[github-actions] ${resource}: dropped ${duplicates} duplicate id(s) — keeping latest copy of each`,\n );\n }\n return Array.from(seen.values());\n}\n\nconst isGitHubSyncCursor = makeChunkedCursorGuard(PHASE_ORDER);\n\nconst workflowRunsResponseSchema = z.object({\n total_count: z.number().int().optional(),\n workflow_runs: z.array(\n z.object({\n id: z.number().int(),\n name: z.string(),\n conclusion: z.string().nullable(),\n status: z.string(),\n head_branch: z.string().nullable(),\n actor: z.object({ login: z.string().min(1) }).nullable(),\n created_at: z.iso.datetime(),\n updated_at: z.iso.datetime(),\n run_attempt: z.number().int(),\n artifacts_url: z.string().optional(),\n cancel_url: z.string().optional(),\n check_suite_id: z.number().int().optional(),\n check_suite_node_id: z.string().optional(),\n check_suite_url: z.string().optional(),\n display_title: z.string().optional(),\n event: z.string().optional(),\n head_commit: z.unknown().optional(),\n head_repository: z.unknown().optional(),\n head_sha: z.string().optional(),\n html_url: z.string().optional(),\n jobs_url: z.string().optional(),\n logs_url: z.string().optional(),\n node_id: z.string().optional(),\n path: z.string().optional(),\n previous_attempt_url: z.string().nullable().optional(),\n pull_requests: z.array(z.unknown()).optional(),\n referenced_workflows: z.array(z.unknown()).optional(),\n repository: z.unknown().optional(),\n rerun_url: z.string().optional(),\n run_number: z.number().int().optional(),\n run_started_at: z.iso.datetime().optional(),\n triggering_actor: z.object({ login: z.string().min(1) }).optional(),\n url: z.string().optional(),\n workflow_id: z.number().int().optional(),\n workflow_url: z.string().optional(),\n }),\n ),\n});\n\nconst pullRequestsSchema = z.array(\n z.object({\n number: z.number().int(),\n title: z.string(),\n state: z.string(),\n draft: z.boolean(),\n user: z.object({\n login: z.string().min(1),\n avatar_url: z.string().optional(),\n events_url: z.string().optional(),\n followers_url: z.string().optional(),\n following_url: z.string().optional(),\n gists_url: z.string().optional(),\n gravatar_id: z.string().nullable().optional(),\n html_url: z.string().optional(),\n id: z.number().int().optional(),\n node_id: z.string().optional(),\n organizations_url: z.string().optional(),\n received_events_url: z.string().optional(),\n repos_url: z.string().optional(),\n site_admin: z.boolean().optional(),\n starred_url: z.string().optional(),\n subscriptions_url: z.string().optional(),\n type: z.string().optional(),\n url: z.string().optional(),\n user_view_type: z.string().optional(),\n }),\n created_at: z.iso.datetime(),\n updated_at: z.iso.datetime(),\n _links: z.unknown().optional(),\n active_lock_reason: z.string().nullable().optional(),\n assignee: z.unknown().optional(),\n assignees: z.unknown().optional(),\n author_association: z.string().optional(),\n auto_merge: z.unknown().optional(),\n base: z.unknown().optional(),\n body: z.string().nullable().optional(),\n closed_at: z.string().nullable().optional(),\n comments_url: z.string().optional(),\n commits_url: z.string().optional(),\n diff_url: z.string().optional(),\n head: z.unknown().optional(),\n html_url: z.string().optional(),\n id: z.number().int().optional(),\n issue_url: z.string().optional(),\n labels: z.unknown().optional(),\n locked: z.boolean().optional(),\n merge_commit_sha: z.string().nullable().optional(),\n merged_at: z.string().nullable().optional(),\n milestone: z.unknown().optional(),\n node_id: z.string().optional(),\n patch_url: z.string().optional(),\n requested_reviewers: z.unknown().optional(),\n requested_teams: z.unknown().optional(),\n review_comment_url: z.string().optional(),\n review_comments_url: z.string().optional(),\n statuses_url: z.string().optional(),\n url: z.string().optional(),\n }),\n);\n\nconst reviewsSchema = z.array(\n z.object({\n user: z.object({ login: z.string().min(1) }).nullable(),\n state: z.string(),\n submitted_at: z.iso.datetime(),\n }),\n);\n\nconst issuesSchema = z.array(\n z.object({\n number: z.number().int(),\n title: z.string(),\n state: z.string(),\n labels: z.array(\n z.object({\n name: z.string(),\n id: z.number().int().optional(),\n node_id: z.string().optional(),\n url: z.string().optional(),\n color: z.string().optional(),\n default: z.boolean().optional(),\n description: z.string().nullable().optional(),\n }),\n ),\n assignees: z.array(z.object({ login: z.string().min(1) })),\n user: z.object({ login: z.string().min(1) }).catchall(z.unknown()),\n created_at: z.iso.datetime(),\n updated_at: z.iso.datetime(),\n closed_at: z.iso.datetime().nullable().optional(),\n pull_request: z.unknown().optional(),\n active_lock_reason: z.unknown().optional(),\n assignee: z.unknown().optional(),\n author_association: z.string().optional(),\n body: z.string().nullable().optional(),\n closed_by: z.unknown().optional(),\n comments: z.number().int().optional(),\n comments_url: z.string().optional(),\n draft: z.boolean().optional(),\n events_url: z.string().optional(),\n html_url: z.string().optional(),\n id: z.number().int().optional(),\n issue_field_values: z.unknown().optional(),\n labels_url: z.string().optional(),\n locked: z.boolean().optional(),\n milestone: z.unknown().optional(),\n node_id: z.string().optional(),\n performed_via_github_app: z.unknown().optional(),\n reactions: z.unknown().optional(),\n repository_url: z.string().optional(),\n state_reason: z.unknown().optional(),\n timeline_url: z.string().optional(),\n type: z.unknown().optional(),\n url: z.string().optional(),\n }),\n);\n\nconst deploymentsSchema = z.array(\n z.object({\n id: z.number().int(),\n environment: z.string(),\n ref: z.string(),\n sha: z.string(),\n creator: z.object({ login: z.string().min(1) }).nullable(),\n created_at: z.iso.datetime(),\n }),\n);\n\nconst deploymentStatusesSchema = z.array(\n z.object({\n state: z.string(),\n updated_at: z.iso.datetime(),\n }),\n);\n\nconst releasesSchema = z.array(\n z.object({\n id: z.number().int(),\n tag_name: z.string(),\n name: z.string().nullable(),\n draft: z.boolean(),\n prerelease: z.boolean(),\n created_at: z.iso.datetime(),\n published_at: z.iso.datetime().nullable(),\n author: z.object({\n login: z.string().min(1),\n id: z.number().int().optional(),\n node_id: z.string().optional(),\n avatar_url: z.string().optional(),\n gravatar_id: z.string().nullable().optional(),\n url: z.string().optional(),\n html_url: z.string().optional(),\n followers_url: z.string().optional(),\n following_url: z.string().optional(),\n gists_url: z.string().optional(),\n starred_url: z.string().optional(),\n subscriptions_url: z.string().optional(),\n organizations_url: z.string().optional(),\n repos_url: z.string().optional(),\n events_url: z.string().optional(),\n received_events_url: z.string().optional(),\n type: z.string().optional(),\n site_admin: z.boolean().optional(),\n user_view_type: z.string().optional(),\n }),\n node_id: z.string().optional(),\n url: z.string().optional(),\n html_url: z.string().optional(),\n assets_url: z.string().optional(),\n upload_url: z.string().optional(),\n tarball_url: z.string().nullable().optional(),\n zipball_url: z.string().nullable().optional(),\n target_commitish: z.string().optional(),\n body: z.string().nullable().optional(),\n immutable: z.boolean().optional(),\n mentions_count: z.number().int().optional(),\n updated_at: z.iso.datetime().optional(),\n assets: z.array(z.unknown()).optional(),\n }),\n);\n\nconst contributorsSchema = z.array(\n z.object({\n total: z.number().int(),\n weeks: z.array(\n z.object({\n w: z.number().int(),\n a: z.number().int(),\n d: z.number().int(),\n c: z.number().int(),\n }),\n ),\n author: z.object({ login: z.string().min(1) }),\n }),\n);\n\nconst repoStatsSchema = z.object({\n stargazers_count: z.number().int(),\n forks_count: z.number().int(),\n subscribers_count: z.number().int(),\n});\n\nexport const githubResources = defineResources({\n repo: {\n shape: 'entity',\n description:\n 'Top-level repository stats (stars, forks, and watchers) as a single entity.',\n endpoint: 'GET /repos/{owner}/{repo}',\n responses: { repo: repoStatsSchema },\n },\n workflow_run: {\n shape: 'event',\n description: 'GitHub Actions CI pipeline executions.',\n endpoint: 'GET /repos/{owner}/{repo}/actions/runs',\n responses: { workflow_runs: workflowRunsResponseSchema },\n },\n pull_request: {\n shape: 'entity',\n description:\n 'Open and closed pull requests, including draft state, author, and review state.',\n endpoint: 'GET /repos/{owner}/{repo}/pulls',\n notes:\n 'Review state is folded in from GET /repos/{owner}/{repo}/pulls/{number}/reviews per PR.',\n filterable: [\n { field: 'state', ops: ['eq'], values: ['open', 'closed', 'merged'] },\n ],\n responses: {\n pull_requests: pullRequestsSchema,\n pull_request_reviews: reviewsSchema,\n },\n },\n issue: {\n shape: 'entity',\n description:\n 'Open and closed issues with labels, assignees, and author (pull requests excluded).',\n endpoint: 'GET /repos/{owner}/{repo}/issues',\n filterable: [{ field: 'state', ops: ['eq'], values: ['open', 'closed'] }],\n responses: { issues: issuesSchema },\n },\n deployment: {\n shape: 'entity',\n description:\n 'Deployments with their latest status, keyed by environment and ref.',\n endpoint: 'GET /repos/{owner}/{repo}/deployments',\n notes:\n 'The latest status is folded in from GET /repos/{owner}/{repo}/deployments/{id}/statuses.',\n responses: {\n deployments: deploymentsSchema,\n deployment_statuses: deploymentStatusesSchema,\n },\n },\n release: {\n shape: 'entity',\n description: 'Published, draft, and prerelease GitHub releases.',\n endpoint: 'GET /repos/{owner}/{repo}/releases',\n responses: { releases: releasesSchema },\n },\n contributor: {\n shape: 'entity',\n description:\n 'Per-author commit activity (commits, additions, deletions) for the repository.',\n endpoint: 'GET /repos/{owner}/{repo}/stats/contributors',\n responses: { contributors: contributorsSchema },\n },\n});\n\nexport const id = 'github-actions';\n\nexport class GitHubConnector extends BaseConnector<\n GitHubSettings,\n GitHubCredentials\n> {\n static readonly id = id;\n\n static readonly resources = githubResources;\n\n static readonly schemas = schemasFromResources(githubResources);\n\n static create(input: unknown, ctx?: ConnectorContext): GitHubConnector {\n const parsed = configFields.parse(input);\n return new GitHubConnector(\n { owner: parsed.owner, repo: parsed.repo },\n { token: parsed.token },\n ctx,\n );\n }\n\n readonly id = id;\n\n override readonly credentials = githubCredentials;\n\n private seenWorkflowRunIds = new Set<string>();\n\n private preservedDeploymentStatus = new Map<string, string>();\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': connectorUserAgent('github'),\n };\n if (this.creds.token) {\n headers['Authorization'] = `Bearer ${this.creds.token}`;\n }\n return headers;\n }\n\n private fetch<T>(\n url: string,\n resource: string,\n signal: AbortSignal | undefined,\n ): Promise<HttpResponse<T>> {\n return this.get<T>(url, {\n resource,\n headers: this.buildHeaders(),\n signal,\n rateLimit: githubRateLimit,\n });\n }\n\n private allowedPageBasePath(phase: GitHubSyncPhase): string | null {\n const { owner, repo } = this.settings;\n switch (phase) {\n case 'workflow_runs':\n return `/repos/${owner}/${repo}/actions/runs`;\n case 'pull_requests':\n return `/repos/${owner}/${repo}/pulls`;\n case 'issues':\n return `/repos/${owner}/${repo}/issues`;\n case 'deployments':\n return `/repos/${owner}/${repo}/deployments`;\n case 'releases':\n return `/repos/${owner}/${repo}/releases`;\n case 'repo_stats':\n case 'contributors':\n return null;\n }\n }\n\n private sanitizePageUrl(\n phase: GitHubSyncPhase,\n pageUrl: string | null,\n ): string | null {\n const allowedPath = this.allowedPageBasePath(phase);\n if (allowedPath === null) {\n return null;\n }\n const canonical = sanitizeAllowedUrl({\n url: pageUrl,\n host: 'api.github.com',\n pathname: allowedPath,\n });\n if (canonical !== null || pageUrl === null) {\n return canonical;\n }\n try {\n const u = new URL(pageUrl);\n const resourceSuffix = allowedPath.replace(/^\\/repos\\/[^/]+\\/[^/]+/, '');\n const escapedSuffix = resourceSuffix.replace(\n /[.*+?^${}()|[\\]\\\\]/g,\n '\\\\$&',\n );\n const numericPath = new RegExp(`^/repositories/\\\\d+${escapedSuffix}$`);\n if (\n u.protocol === 'https:' &&\n u.host === 'api.github.com' &&\n numericPath.test(u.pathname)\n ) {\n return u.toString();\n }\n } catch {\n return null;\n }\n return null;\n }\n\n private isResourceAllowed(options: SyncOptions, resource: string): boolean {\n if (!options.resources) {\n return true;\n }\n return options.resources.has(resource);\n }\n\n private resolveCursor(cursor: unknown): GitHubSyncCursor | undefined {\n if (!isGitHubSyncCursor(cursor)) {\n return undefined;\n }\n return {\n phase: cursor.phase,\n page: this.sanitizePageUrl(cursor.phase, cursor.page),\n spec: cursor.spec,\n };\n }\n\n private specsForResource(\n options: SyncOptions,\n resource: string,\n ): FetchSpec[] {\n const specs = options.fetchSpecs?.[resource];\n return specs && specs.length > 0 ? specs : [{}];\n }\n\n private specCutoff(\n options: SyncOptions,\n resource: string,\n spec: FetchSpec,\n now: number,\n ): number | null {\n if (options.fetchSpecs?.[resource]) {\n return resolveSpecCutoff(spec.requiredWindowMs, now);\n }\n return resolveBackfillCutoff(options, resource, now);\n }\n\n private async fetchRepoStats(\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n const res = await this.fetch<GitHubRepo>(\n `https://api.github.com/repos/${owner}/${repo}`,\n 'repo',\n signal,\n );\n return { items: [res.body], next: null };\n }\n\n private async fetchWorkflowRunsLatest(\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n const res = await this.fetch<GitHubRunsResponse>(\n `https://api.github.com/repos/${owner}/${repo}/actions/runs?per_page=1`,\n 'workflow_runs',\n signal,\n );\n const run = res.body.workflow_runs[0];\n return { items: run ? [run] : [], next: null };\n }\n\n private async fetchWorkflowRunsFull(\n page: string | null,\n signal: AbortSignal | undefined,\n cutoff: number | null,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n const url =\n page ??\n `https://api.github.com/repos/${owner}/${repo}/actions/runs?per_page=100`;\n const res = await this.fetch<GitHubRunsResponse>(\n url,\n 'workflow_runs',\n signal,\n );\n const nextLink = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n const runs = res.body.workflow_runs;\n\n const filtered = runs.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\n const lastRun = runs.at(-1);\n const cutoffReached =\n cutoff !== null &&\n lastRun !== undefined &&\n new Date(lastRun.created_at).getTime() < cutoff &&\n new Date(lastRun.updated_at).getTime() < cutoff;\n\n return {\n items: filtered,\n next: cutoffReached ? null : nextLink,\n };\n }\n\n private async fetchPullRequests(\n options: SyncOptions,\n page: string | null,\n signal: AbortSignal | undefined,\n spec: FetchSpec,\n cutoff: number | null,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n const state = pushableState(spec.filter) ?? 'all';\n const url =\n page ??\n `https://api.github.com/repos/${owner}/${repo}/pulls?state=${state}&sort=updated&direction=desc&per_page=100`;\n const res = await this.fetch<GitHubPR[]>(url, 'pull_requests', signal);\n const nextLink = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n const prs = res.body;\n const filteredPrs =\n cutoff !== null\n ? prs.filter((pr) => new Date(pr.updated_at).getTime() >= cutoff)\n : prs;\n const lastPr = prs.at(-1);\n const cutoffReached =\n cutoff !== null &&\n lastPr !== undefined &&\n new Date(lastPr.updated_at).getTime() < cutoff;\n\n const reviewsByPR = new Map<number, GitHubReview[]>();\n if (this.isResourceAllowed(options, 'pull_request_reviews')) {\n for (const pr of filteredPrs) {\n signal?.throwIfAborted();\n const reviews = await this.fetch<GitHubReview[]>(\n `https://api.github.com/repos/${owner}/${repo}/pulls/${pr.number}/reviews`,\n 'pull_request_reviews',\n signal,\n );\n reviewsByPR.set(pr.number, reviews.body);\n }\n }\n\n const items: PRPageItems[] = [{ prs: filteredPrs, reviewsByPR }];\n return { items, next: cutoffReached ? null : nextLink };\n }\n\n private async fetchIssues(\n page: string | null,\n signal: AbortSignal | undefined,\n spec: FetchSpec,\n cutoff: number | null,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n let url: string;\n if (page) {\n url = page;\n } else {\n const u = new URL(`https://api.github.com/repos/${owner}/${repo}/issues`);\n u.searchParams.set('state', pushableState(spec.filter) ?? 'all');\n u.searchParams.set('per_page', '100');\n if (cutoff !== null) {\n u.searchParams.set('since', new Date(cutoff).toISOString());\n }\n url = u.toString();\n }\n const res = await this.fetch<GitHubIssue[]>(url, 'issues', signal);\n const nextLink = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n return { items: res.body, next: nextLink };\n }\n\n private async fetchDeployments(\n options: SyncOptions,\n page: string | null,\n signal: AbortSignal | undefined,\n cutoff: number | null,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n const url =\n page ??\n `https://api.github.com/repos/${owner}/${repo}/deployments?per_page=100`;\n const res = await this.fetch<GitHubDeployment[]>(\n url,\n 'deployments',\n signal,\n );\n const nextLink = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n const deployments = res.body;\n const filteredDeployments =\n cutoff !== null\n ? deployments.filter((d) => new Date(d.created_at).getTime() >= cutoff)\n : deployments;\n const lastDeployment = deployments.at(-1);\n const cutoffReached =\n cutoff !== null &&\n lastDeployment !== undefined &&\n new Date(lastDeployment.created_at).getTime() < cutoff;\n\n const latestStatusById = new Map<number, GitHubDeploymentStatus | null>();\n if (this.isResourceAllowed(options, 'deployment_statuses')) {\n for (const deployment of filteredDeployments) {\n signal?.throwIfAborted();\n const statusRes = await this.fetch<GitHubDeploymentStatus[]>(\n `https://api.github.com/repos/${owner}/${repo}/deployments/${deployment.id}/statuses?per_page=1`,\n 'deployment_statuses',\n signal,\n );\n latestStatusById.set(deployment.id, statusRes.body[0] ?? null);\n }\n }\n\n const items: DeploymentPageItems[] = [\n { deployments: filteredDeployments, latestStatusById },\n ];\n return { items, next: cutoffReached ? null : nextLink };\n }\n\n private async fetchReleases(\n page: string | null,\n signal: AbortSignal | undefined,\n cutoff: number | null,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n const url =\n page ??\n `https://api.github.com/repos/${owner}/${repo}/releases?per_page=100`;\n const res = await this.fetch<GitHubRelease[]>(url, 'releases', signal);\n const nextLink = parseLinkHeader(res.headers.get('link'))['next'] ?? null;\n const releases = res.body;\n const filtered =\n cutoff !== null\n ? releases.filter((r) => {\n const ts = new Date(r.published_at ?? r.created_at).getTime();\n return ts >= cutoff;\n })\n : releases;\n const lastRelease = releases.at(-1);\n const cutoffReached =\n cutoff !== null &&\n lastRelease !== undefined &&\n new Date(lastRelease.published_at ?? lastRelease.created_at).getTime() <\n cutoff;\n return { items: filtered, next: cutoffReached ? null : nextLink };\n }\n\n private async fetchContributors(\n signal: AbortSignal | undefined,\n ): Promise<FetchPageResult<string>> {\n const { owner, repo } = this.settings;\n const contributors = await this.withRetry<GitHubContributorStats[]>(\n async (sig) => {\n const res = await this.fetch<GitHubContributorStats[] | null>(\n `https://api.github.com/repos/${owner}/${repo}/stats/contributors`,\n '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) {\n console.warn(\n '[github-actions] Stats endpoint never became ready — skipping contributor sync and keeping previous data.',\n );\n return { items: [CONTRIBUTORS_SKIPPED], next: null };\n }\n return { items: contributors, next: null };\n }\n\n private async writeRepoStats(\n storage: StorageHandle,\n items: unknown[],\n ): Promise<void> {\n const repoBody = items[0] as GitHubRepo | undefined;\n if (!repoBody) {\n return;\n }\n const { owner, repo } = this.settings;\n await storage.entities(\n [\n {\n type: 'repo',\n id: `${owner}/${repo}`,\n attributes: {\n stars: repoBody.stargazers_count,\n forks: repoBody.forks_count,\n watchers: repoBody.subscribers_count,\n },\n updated_at: Date.now(),\n },\n ],\n { types: ['repo'] },\n );\n }\n\n private async writeWorkflowRunsLatest(\n storage: StorageHandle,\n items: unknown[],\n ): Promise<void> {\n const run = items[0] as\n | GitHubRunsResponse['workflow_runs'][number]\n | undefined;\n if (!run) {\n return;\n }\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\n private async writeWorkflowRunsFull(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n spec: number,\n ): Promise<void> {\n if (page === null && spec === 0) {\n await storage.events([], { names: ['workflow_run'] });\n this.seenWorkflowRunIds.clear();\n }\n const withinPage = dedupeByKey(\n items as GitHubRunsResponse['workflow_runs'],\n (run) => String(run.id),\n 'workflow_runs',\n );\n let crossPageDuplicates = 0;\n const runs: GitHubRunsResponse['workflow_runs'] = [];\n for (const run of withinPage) {\n const key = String(run.id);\n if (this.seenWorkflowRunIds.has(key)) {\n crossPageDuplicates++;\n continue;\n }\n this.seenWorkflowRunIds.add(key);\n runs.push(run);\n }\n if (crossPageDuplicates > 0) {\n console.warn(\n `[github-actions] workflow_runs: dropped ${crossPageDuplicates} duplicate id(s) seen on an earlier page`,\n );\n }\n for (const run of runs) {\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 }\n\n private async writePullRequests(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n spec: number,\n options: SyncOptions,\n ): Promise<void> {\n const reviewsAllowed = this.isResourceAllowed(\n options,\n 'pull_request_reviews',\n );\n if (page === null && spec === 0) {\n await storage.entities([], { types: ['pull_request'] });\n if (reviewsAllowed) {\n await storage.edges([], { kinds: ['reviewed_by'] });\n }\n }\n const pageItems = items as PRPageItems[];\n for (const { prs: rawPrs, reviewsByPR } of pageItems) {\n const prs = dedupeByKey(\n rawPrs,\n (pr) => String(pr.number),\n 'pull_requests',\n );\n for (const pr of prs) {\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 if (!reviewsAllowed) {\n continue;\n }\n for (const pr of prs) {\n const reviews = reviewsByPR.get(pr.number) ?? [];\n for (const review of reviews) {\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 }\n\n private async writeIssues(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n spec: number,\n ): Promise<void> {\n if (page === null && spec === 0) {\n await storage.entities([], { types: ['issue'] });\n }\n const issues = dedupeByKey(\n (items as GitHubIssue[]).filter((i) => i.pull_request === undefined),\n (issue) => String(issue.number),\n 'issues',\n );\n for (const issue of issues) {\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\n private async writeDeployments(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n spec: number,\n options: SyncOptions,\n ): Promise<void> {\n const statusesAllowed = this.isResourceAllowed(\n options,\n 'deployment_statuses',\n );\n if (page === null && spec === 0) {\n if (!statusesAllowed) {\n const existing = await storage.queryEntities({ type: 'deployment' });\n for (const entity of existing) {\n const prev = entity.attributes['latest_status'];\n if (typeof prev === 'string') {\n this.preservedDeploymentStatus.set(entity.id, prev);\n }\n }\n }\n await storage.entities([], { types: ['deployment'] });\n }\n const pageItems = items as DeploymentPageItems[];\n for (const { deployments: rawDeployments, latestStatusById } of pageItems) {\n const deployments = dedupeByKey(\n rawDeployments,\n (d) => String(d.id),\n 'deployments',\n );\n for (const deployment of deployments) {\n const createdMs = new Date(deployment.created_at).getTime();\n let latestStatus: string;\n let statusUpdatedMs: number | null = null;\n if (statusesAllowed) {\n const status = latestStatusById.get(deployment.id) ?? null;\n latestStatus = status?.state ?? 'unknown';\n statusUpdatedMs = status?.updated_at\n ? new Date(status.updated_at).getTime()\n : null;\n } else {\n latestStatus =\n this.preservedDeploymentStatus.get(String(deployment.id)) ??\n 'unknown';\n }\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: latestStatus,\n },\n updated_at: Math.max(createdMs, statusUpdatedMs ?? 0),\n });\n }\n }\n }\n\n private async writeReleases(\n storage: StorageHandle,\n items: unknown[],\n page: string | null,\n spec: number,\n ): Promise<void> {\n if (page === null && spec === 0) {\n await storage.entities([], { types: ['release'] });\n }\n const releases = dedupeByKey(\n items as GitHubRelease[],\n (r) => String(r.id),\n 'releases',\n );\n for (const release of releases) {\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\n private async writeContributors(\n storage: StorageHandle,\n items: unknown[],\n ): Promise<void> {\n if (items[0] === CONTRIBUTORS_SKIPPED) {\n return;\n }\n const contributors = dedupeByKey(\n items as GitHubContributorStats[],\n (c) => c.author.login,\n 'contributors',\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 async sync(\n options: SyncOptions,\n storage: StorageHandle,\n signal?: AbortSignal,\n ): Promise<SyncResult> {\n const cursor = this.resolveCursor(options.cursor);\n const phases = selectPhases(options.resources);\n const specCount = (phase: GitHubSyncPhase): number => {\n if (options.mode === 'latest' || SINGLE_SPEC_PHASES.has(phase)) {\n return 1;\n }\n return this.specsForResource(options, PHASE_RESOURCES[phase][0]!).length;\n };\n const specFor = (phase: GitHubSyncPhase, specIndex: number): FetchSpec =>\n this.specsForResource(options, PHASE_RESOURCES[phase][0]!)[specIndex] ??\n {};\n const cutoffFor = (\n phase: GitHubSyncPhase,\n spec: FetchSpec,\n ): number | null =>\n this.specCutoff(options, PHASE_RESOURCES[phase][0]!, spec, Date.now());\n return paginateChunked<GitHubSyncPhase, string>({\n phases,\n cursor,\n signal,\n specCount,\n logger: this.logger,\n fetchPage: async (phase, page, sig, specIndex) => {\n const spec = specFor(phase, specIndex);\n switch (phase) {\n case 'repo_stats':\n return this.fetchRepoStats(sig);\n case 'workflow_runs':\n return options.mode === 'latest'\n ? this.fetchWorkflowRunsLatest(sig)\n : this.fetchWorkflowRunsFull(page, sig, cutoffFor(phase, spec));\n case 'pull_requests':\n return this.fetchPullRequests(\n options,\n page,\n sig,\n spec,\n cutoffFor(phase, spec),\n );\n case 'issues':\n return this.fetchIssues(page, sig, spec, cutoffFor(phase, spec));\n case 'deployments':\n return this.fetchDeployments(\n options,\n page,\n sig,\n cutoffFor(phase, spec),\n );\n case 'releases':\n return this.fetchReleases(page, sig, cutoffFor(phase, spec));\n case 'contributors':\n return this.fetchContributors(sig);\n }\n },\n writeBatch: async (phase, items, page, specIndex) => {\n switch (phase) {\n case 'repo_stats':\n return this.writeRepoStats(storage, items);\n case 'workflow_runs':\n return options.mode === 'latest'\n ? this.writeWorkflowRunsLatest(storage, items)\n : this.writeWorkflowRunsFull(storage, items, page, specIndex);\n case 'pull_requests':\n return this.writePullRequests(\n storage,\n items,\n page,\n specIndex,\n options,\n );\n case 'issues':\n return this.writeIssues(storage, items, page, specIndex);\n case 'deployments':\n return this.writeDeployments(\n storage,\n items,\n page,\n specIndex,\n options,\n );\n case 'releases':\n return this.writeReleases(storage, items, page, specIndex);\n case 'contributors':\n return this.writeContributors(storage, items);\n }\n },\n });\n }\n}\n","import { GitHubConnector } from './github';\n\nexport {\n configFields,\n doc,\n GitHubConnector,\n githubResources as resources,\n id,\n} from './github';\nexport type { GitHubSettings } from './github';\nexport default GitHubConnector;\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rawdash/connector-github",
3
- "version": "0.21.1",
3
+ "version": "0.22.0",
4
4
  "description": "Rawdash connector for GitHub",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "zod": "^4.4.3",
27
- "@rawdash/core": "0.21.1"
27
+ "@rawdash/core": "0.22.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "fast-check": "^4.8.0",
@@ -32,7 +32,7 @@
32
32
  "typescript": "^5.7.2",
33
33
  "vitest": "^4.1.4",
34
34
  "@rawdash/connector-shared": "0.3.1",
35
- "@rawdash/connector-test-utils": "0.0.9"
35
+ "@rawdash/connector-test-utils": "0.0.10"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "tsup",