@goodfellow-cms/github 0.6.0 → 0.7.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.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ConflictError, DomainError, DraftError, EditorsError, GitApiError, NIGHTLY_VARIABLE, PluginTaskError, REBUILD_JOB, SetupError, SignInError, TASK_BRANCH_PREFIX, TASK_EVENT, TASK_POLL_MS, TASK_RESULT_FILE, TASK_TIMEOUT_MS, TASK_WORKFLOW, UpdatesError, assertContentChanges, branchDrafts, buildStepFor, compareReleases, credentialStorage, decodeBase64, encodeBase64, encodeBase64Bytes, hostSource, nightlyMode, siteVersion, taskRequestProblem } from "@goodfellow-cms/core";
1
+ import { ConflictError, DomainError, DraftError, EditorsError, GitApiError, NIGHTLY_VARIABLE, PluginTaskError, REBUILD_JOB, SetupError, SignInError, TASK_BRANCH_PREFIX, TASK_EVENT, TASK_POLL_MS, TASK_RESULT_FILE, TASK_TIMEOUT_MS, TASK_WORKFLOW, UpdatesError, assertContentChanges, branchDrafts, buildStepFor, compareReleases, credentialStorage, decodeBase64, encodeBase64, encodeBase64Bytes, hostSource, nightlyMode, siteVersion, taskRequestProblem, withRateLimits } from "@goodfellow-cms/core";
2
2
  //#region src/api.ts
3
3
  /** Calls the GitHub REST or GraphQL API. Treats 401 as an expired sign-in; other errors become `GitApiError`. */
4
4
  async function githubRequest({ apiUrl, token, fetch }, path, init = {}) {
@@ -703,6 +703,14 @@ var GitHubBackend = class GitHubBackend {
703
703
  draftHost() {
704
704
  const base = `/repos/${this.repo}`;
705
705
  const ref = (branch) => `${base}/git/refs/heads/${encodeURIComponent(branch)}`;
706
+ const openPulls = (branch) => {
707
+ const owner = decodeURIComponent(this.repo).split("/")[0];
708
+ const query = new URLSearchParams({
709
+ head: `${owner}:${branch}`,
710
+ state: "open"
711
+ });
712
+ return githubJson(this.api, `${base}/pulls?${query}`);
713
+ };
706
714
  return {
707
715
  liveBranch: this.branch,
708
716
  branchStore: (branch) => new GitHubBackend({
@@ -733,7 +741,7 @@ var GitHubBackend = class GitHubBackend {
733
741
  listBranches: async (prefix) => {
734
742
  return (await githubJson(this.api, `${base}/git/matching-refs/heads/${encodeURIComponent(prefix)}`)).map((found) => found.ref.replace(/^refs\/heads\//, ""));
735
743
  },
736
- openReview: async ({ branch, title, description }) => {
744
+ openReview: async ({ branch, title, description, reviewers = [] }) => {
737
745
  const response = await githubRequest(this.api, `${base}/pulls`, {
738
746
  method: "POST",
739
747
  body: {
@@ -748,16 +756,46 @@ var GitHubBackend = class GitHubBackend {
748
756
  422
749
757
  ]
750
758
  });
751
- if (response.ok) return { url: (await response.json()).html_url };
752
- if (response.status !== 422) throw new DraftError("no-reviews", "This sign-in can't open pull requests on the site's repository.");
753
- const owner = decodeURIComponent(this.repo).split("/")[0];
754
- const query = new URLSearchParams({
755
- head: `${owner}:${branch}`,
756
- state: "open"
759
+ let review;
760
+ if (response.ok) review = await response.json();
761
+ else if (response.status !== 422) throw new DraftError("no-reviews", "This sign-in can't open pull requests on the site's repository.");
762
+ else {
763
+ [review] = await openPulls(branch);
764
+ if (!review) throw new GitApiError(422, "GitHub couldn't open the pull request.");
765
+ }
766
+ for (const reviewer of reviewers) await githubRequest(this.api, `${base}/pulls/${review.number}/requested_reviewers`, {
767
+ method: "POST",
768
+ body: { reviewers: [reviewer] },
769
+ allow: [
770
+ 403,
771
+ 404,
772
+ 422
773
+ ]
774
+ });
775
+ return { url: review.html_url };
776
+ },
777
+ listReviews: async (prefix) => {
778
+ const pulls = await githubJson(this.api, `${base}/pulls?${new URLSearchParams({
779
+ state: "open",
780
+ per_page: "100"
781
+ })}`);
782
+ const repo = decodeURIComponent(this.repo).toLowerCase();
783
+ return pulls.filter((pull) => pull.head.ref.startsWith(prefix) && (pull.head.repo?.full_name ?? repo).toLowerCase() === repo).map((pull) => ({
784
+ branch: pull.head.ref,
785
+ url: pull.html_url,
786
+ title: pull.title
787
+ }));
788
+ },
789
+ closeReview: async (branch) => {
790
+ for (const pull of await openPulls(branch)) await githubRequest(this.api, `${base}/pulls/${pull.number}`, {
791
+ method: "PATCH",
792
+ body: { state: "closed" },
793
+ allow: [
794
+ 403,
795
+ 404,
796
+ 422
797
+ ]
757
798
  });
758
- const [open] = await githubJson(this.api, `${base}/pulls?${query}`);
759
- if (!open) throw new GitApiError(422, "GitHub couldn't open the pull request.");
760
- return { url: open.html_url };
761
799
  }
762
800
  };
763
801
  }
@@ -1820,7 +1858,7 @@ function github(options) {
1820
1858
  const links = githubTokenLinks(options.repo, options.webUrl);
1821
1859
  const storage = credentialStorage(`goodfellow:github:${options.repo}`, options.storage);
1822
1860
  const pending = credentialStorage(`goodfellow:github:pending:${options.repo}`, options.storage);
1823
- const fetcher = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
1861
+ const fetcher = withRateLimits(options.fetch ?? ((input, init) => globalThis.fetch(input, init)), { host: "GitHub" });
1824
1862
  const location = options.location ?? browserLocation();
1825
1863
  const signInUrl = options.signIn;
1826
1864
  const service = signInUrl && location && !isLocalAddress(location.href()) ? new SignInService(signInUrl, fetcher) : void 0;
package/dist/testing.js CHANGED
@@ -870,9 +870,33 @@ function fakeGitHub(options) {
870
870
  const head = url.searchParams.get("head")?.split(":")[1];
871
871
  return json(repo.reviews.filter((review) => review.state === "open" && (!head || review.branch === head)).map((review) => ({
872
872
  number: review.number,
873
- html_url: `https://github.com/${options.repo}/pull/${review.number}`
873
+ title: review.title,
874
+ html_url: `https://github.com/${options.repo}/pull/${review.number}`,
875
+ head: {
876
+ ref: review.branch,
877
+ repo: { full_name: options.repo }
878
+ }
874
879
  })));
875
880
  }
881
+ const pull = rest.match(/^\/pulls\/(\d+)(\/requested_reviewers)?$/);
882
+ if (pull) {
883
+ const review = repo.reviews.find((found) => found.number === Number(pull[1]));
884
+ if (!review) return json({ message: "Not Found" }, 404);
885
+ if (user.bot && user.permissions?.pull_requests !== "write") return json({ message: "Resource not accessible by integration" }, 403);
886
+ if (pull[2] && request.method === "POST") {
887
+ const body = await request.json();
888
+ if ((body.reviewers ?? []).some((login) => login === user.login || login.startsWith("not-"))) return json({ message: "Reviews may only be requested from collaborators." }, 422);
889
+ review.reviewers.push(...body.reviewers ?? []);
890
+ return json({ number: review.number }, 201);
891
+ }
892
+ if (!pull[2] && request.method === "PATCH") {
893
+ if ((await request.json()).state === "closed") review.state = "closed";
894
+ return json({
895
+ number: review.number,
896
+ state: review.state
897
+ });
898
+ }
899
+ }
876
900
  if (rest === "/pages" && request.method === "GET") {
877
901
  const site = pages.current;
878
902
  if (!site) return json({ message: "Not Found" }, 404);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodfellow-cms/github",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Edit a Goodfellow site stored on GitHub, straight from the browser.",
5
5
  "keywords": [
6
6
  "goodfellow",
@@ -35,7 +35,7 @@
35
35
  "dist"
36
36
  ],
37
37
  "dependencies": {
38
- "@goodfellow-cms/core": "0.6.0",
38
+ "@goodfellow-cms/core": "0.7.0",
39
39
  "@noble/ciphers": "2.4.0",
40
40
  "@noble/hashes": "2.4.0"
41
41
  },