@emulators/github 0.4.0 → 0.5.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
@@ -37,7 +37,10 @@ function getGitHubStore(store) {
37
37
  checkSuites: store.collection("github.check_suites", ["repo_id", "head_sha"]),
38
38
  oauthApps: store.collection("github.oauth_apps", ["client_id"]),
39
39
  apps: store.collection("github.apps", ["slug"]),
40
- appInstallations: store.collection("github.app_installations", ["app_id", "installation_id"]),
40
+ appInstallations: store.collection("github.app_installations", [
41
+ "app_id",
42
+ "installation_id"
43
+ ]),
41
44
  oauthGrants: store.collection("github.oauth_grants", ["user_id", "client_id"])
42
45
  };
43
46
  }
@@ -744,6 +747,7 @@ var FONTS = {
744
747
  "geist-sans.woff2": readFileSync(join(__dirname, "fonts", "geist-sans.woff2")),
745
748
  "GeistPixel-Square.woff2": readFileSync(join(__dirname, "fonts", "GeistPixel-Square.woff2"))
746
749
  };
750
+ var FAVICON = readFileSync(join(__dirname, "fonts", "favicon.ico"));
747
751
  function parsePagination(c) {
748
752
  const page = Math.max(1, parseInt(c.req.query("page") ?? "1", 10) || 1);
749
753
  const per_page = Math.min(100, Math.max(1, parseInt(c.req.query("per_page") ?? "30", 10) || 30));
@@ -921,6 +925,132 @@ body{
921
925
  .app-link-name{font-weight:600;font-size:.875rem;color:#33ff00;}
922
926
  .app-link-scopes{font-size:.6875rem;color:#1a8c00;margin-top:1px;}
923
927
  .empty{color:#1a8c00;text-align:center;padding:28px 0;font-size:.875rem;}
928
+
929
+ .inspector-layout{max-width:960px;margin:0 auto;padding:28px 20px;}
930
+ .inspector-tabs{display:flex;gap:4px;margin-bottom:20px;}
931
+ .inspector-tabs a{
932
+ padding:7px 16px;border-radius:6px;text-decoration:none;
933
+ font-size:.8125rem;color:#1a8c00;border:1px solid transparent;
934
+ transition:color .15s,border-color .15s;
935
+ }
936
+ .inspector-tabs a:hover{color:#33ff00;}
937
+ .inspector-tabs a.active{color:#33ff00;font-weight:600;border-color:#0a3300;background:#0a3300;}
938
+ .inspector-section{margin-bottom:24px;}
939
+ .inspector-section h2{
940
+ font-family:'Geist Pixel',monospace;
941
+ font-size:1rem;font-weight:600;color:#33ff00;margin-bottom:10px;
942
+ }
943
+ .inspector-section h3{
944
+ font-family:'Geist Pixel',monospace;
945
+ font-size:.875rem;font-weight:600;color:#1a8c00;margin:16px 0 8px;
946
+ }
947
+ .inspector-table{width:100%;border-collapse:collapse;margin-bottom:12px;}
948
+ .inspector-table th,.inspector-table td{
949
+ text-align:left;padding:8px 12px;border-bottom:1px solid #0a3300;
950
+ font-size:.8125rem;
951
+ }
952
+ .inspector-table th{color:#1a8c00;font-weight:600;font-size:.75rem;text-transform:uppercase;letter-spacing:.04em;}
953
+ .inspector-table td{color:#33ff00;}
954
+ .inspector-table tbody tr{transition:background .1s;}
955
+ .inspector-table tbody tr:hover{background:#0a3300;}
956
+ .inspector-empty{color:#1a8c00;text-align:center;padding:20px 0;font-size:.8125rem;}
957
+
958
+ .checkout-layout{
959
+ display:flex;min-height:calc(100vh - 42px);
960
+ }
961
+ .checkout-summary{
962
+ flex:1;background:#020;padding:48px 40px 48px 10%;
963
+ display:flex;flex-direction:column;justify-content:center;
964
+ border-right:1px solid #0a3300;
965
+ }
966
+ .checkout-form-side{
967
+ flex:1;background:#000;padding:48px 10% 48px 40px;
968
+ display:flex;flex-direction:column;justify-content:center;
969
+ }
970
+ .checkout-merchant{
971
+ display:flex;align-items:center;gap:10px;margin-bottom:6px;
972
+ }
973
+ .checkout-merchant-name{
974
+ font-family:'Geist Pixel',monospace;
975
+ font-size:.9375rem;font-weight:600;color:#33ff00;
976
+ }
977
+ .checkout-test-badge{
978
+ font-size:.625rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
979
+ background:#0a3300;color:#1a8c00;padding:2px 8px;border-radius:4px;
980
+ }
981
+ .checkout-total{
982
+ font-family:'Geist Pixel',monospace;
983
+ font-size:2rem;font-weight:700;color:#33ff00;margin:8px 0 28px;
984
+ }
985
+ .checkout-line-item{
986
+ display:flex;align-items:center;gap:14px;padding:14px 0;
987
+ border-bottom:1px solid #0a3300;
988
+ }
989
+ .checkout-line-item:first-child{border-top:1px solid #0a3300;}
990
+ .checkout-item-icon{
991
+ width:42px;height:42px;border-radius:6px;background:#0a3300;
992
+ display:flex;align-items:center;justify-content:center;flex-shrink:0;
993
+ font-family:'Geist Pixel',monospace;font-size:.875rem;font-weight:700;color:#116600;
994
+ }
995
+ .checkout-item-details{flex:1;min-width:0;}
996
+ .checkout-item-name{font-size:.875rem;font-weight:600;color:#33ff00;}
997
+ .checkout-item-qty{font-size:.75rem;color:#1a8c00;margin-top:2px;}
998
+ .checkout-item-price{
999
+ font-size:.875rem;font-weight:600;color:#33ff00;text-align:right;white-space:nowrap;
1000
+ }
1001
+ .checkout-item-unit{font-size:.6875rem;color:#1a8c00;text-align:right;margin-top:2px;}
1002
+ .checkout-totals{margin-top:20px;}
1003
+ .checkout-totals-row{
1004
+ display:flex;justify-content:space-between;padding:6px 0;
1005
+ font-size:.8125rem;color:#1a8c00;
1006
+ }
1007
+ .checkout-totals-row.total{
1008
+ border-top:1px solid #0a3300;margin-top:8px;padding-top:14px;
1009
+ font-size:.9375rem;font-weight:600;color:#33ff00;
1010
+ }
1011
+ .checkout-form-section{margin-bottom:24px;}
1012
+ .checkout-form-label{
1013
+ font-size:.8125rem;font-weight:600;color:#33ff00;margin-bottom:8px;display:block;
1014
+ }
1015
+ .checkout-input{
1016
+ width:100%;padding:10px 12px;border:1px solid #0a3300;border-radius:6px;
1017
+ background:#020;color:#33ff00;font:inherit;font-size:.875rem;
1018
+ transition:border-color .15s;outline:none;
1019
+ }
1020
+ .checkout-input:focus{border-color:#33ff00;}
1021
+ .checkout-input::placeholder{color:#116600;}
1022
+ .checkout-card-box{
1023
+ border:1px solid #0a3300;border-radius:6px;padding:14px;
1024
+ background:#020;
1025
+ }
1026
+ .checkout-card-row{
1027
+ display:flex;gap:12px;margin-top:10px;
1028
+ }
1029
+ .checkout-card-row .checkout-input{flex:1;}
1030
+ .checkout-sim-note{
1031
+ font-size:.6875rem;color:#1a8c00;margin-top:10px;text-align:center;
1032
+ font-style:italic;
1033
+ }
1034
+ .checkout-pay-btn{
1035
+ width:100%;padding:14px;border:none;border-radius:8px;
1036
+ background:#33ff00;color:#000;font:inherit;font-size:.9375rem;font-weight:700;
1037
+ cursor:pointer;transition:background .15s;
1038
+ font-family:'Geist Pixel',monospace;
1039
+ }
1040
+ .checkout-pay-btn:hover{background:#44ff22;}
1041
+ .checkout-cancel{
1042
+ text-align:center;margin-top:14px;
1043
+ }
1044
+ .checkout-cancel a{
1045
+ color:#1a8c00;text-decoration:none;font-size:.8125rem;
1046
+ transition:color .15s;
1047
+ }
1048
+ .checkout-cancel a:hover{color:#33ff00;}
1049
+ @media(max-width:768px){
1050
+ .checkout-layout{flex-direction:column;}
1051
+ .checkout-summary{padding:32px 20px;border-right:none;border-bottom:1px solid #0a3300;}
1052
+ .checkout-form-side{padding:32px 20px;}
1053
+ }
924
1054
  `;
925
1055
  var POWERED_BY = `<div class="powered-by">Powered by <a href="https://emulate.dev" target="_blank" rel="noopener">emulate</a></div>`;
926
1056
  function emuBar(service) {
@@ -940,6 +1070,7 @@ function head(title) {
940
1070
  <head>
941
1071
  <meta charset="utf-8"/>
942
1072
  <meta name="viewport" content="width=device-width,initial-scale=1"/>
1073
+ <link rel="icon" href="/_emulate/favicon.ico"/>
943
1074
  <title>${escapeHtml(title)} | emulate</title>
944
1075
  <style>${CSS}</style>
945
1076
  </head>`;
@@ -1049,9 +1180,7 @@ function canAccessRepo(gh, authUser, repo) {
1049
1180
  if (!user) return false;
1050
1181
  if (repo.owner_type === "User" && repo.owner_id === user.id) return true;
1051
1182
  if (repo.owner_type === "Organization" && isOrgMember(gh, user.id, repo.owner_id)) return true;
1052
- return Boolean(
1053
- gh.collaborators.findBy("repo_id", repo.id).find((c) => c.user_id === user.id)
1054
- );
1183
+ return Boolean(gh.collaborators.findBy("repo_id", repo.id).find((c) => c.user_id === user.id));
1055
1184
  }
1056
1185
  function assertRepoRead(gh, authUser, repo) {
1057
1186
  if (canAccessRepo(gh, authUser, repo)) return;
@@ -1092,9 +1221,7 @@ function assertIssueWrite(gh, authUser, repo) {
1092
1221
 
1093
1222
  // src/routes/users.ts
1094
1223
  function listReposForUser(gh, user, type) {
1095
- const owned = gh.repos.all().filter(
1096
- (r) => r.owner_id === user.id && r.owner_type === "User"
1097
- );
1224
+ const owned = gh.repos.all().filter((r) => r.owner_id === user.id && r.owner_type === "User");
1098
1225
  const member = gh.collaborators.findBy("user_id", user.id).map((c) => gh.repos.get(c.repo_id)).filter((r) => Boolean(r)).filter((r) => !(r.owner_id === user.id && r.owner_type === "User"));
1099
1226
  if (type === "owner") return owned;
1100
1227
  if (type === "member") return member;
@@ -1222,10 +1349,7 @@ function usersRoutes({ app, store, baseUrl }) {
1222
1349
  });
1223
1350
  app.get("/users", (c) => {
1224
1351
  const since = Math.max(0, parseInt(c.req.query("since") ?? "0", 10) || 0);
1225
- const perPage = Math.min(
1226
- 100,
1227
- Math.max(1, parseInt(c.req.query("per_page") ?? "30", 10) || 30)
1228
- );
1352
+ const perPage = Math.min(100, Math.max(1, parseInt(c.req.query("per_page") ?? "30", 10) || 30));
1229
1353
  const ordered = gh.users.all().filter((u) => u.id > since).sort((a, b) => a.id - b.id);
1230
1354
  const page = ordered.slice(0, perPage);
1231
1355
  if (page.length === perPage && ordered.length > perPage) {
@@ -1813,7 +1937,7 @@ function reposRoutes({ app, store, webhooks, baseUrl }) {
1813
1937
  let ownerType = "User";
1814
1938
  let ownerId = user.id;
1815
1939
  let fullName = "";
1816
- let forkName = typeof body.name === "string" && body.name.trim() ? validateRepoName(body.name) : parent.name;
1940
+ const forkName = typeof body.name === "string" && body.name.trim() ? validateRepoName(body.name) : parent.name;
1817
1941
  if (typeof body.organization === "string" && body.organization.trim()) {
1818
1942
  const org = gh.orgs.findOneBy("login", body.organization.trim());
1819
1943
  if (!org) throw notFound();
@@ -1896,9 +2020,7 @@ function reposRoutes({ app, store, webhooks, baseUrl }) {
1896
2020
  const u = gh.users.get(col.user_id);
1897
2021
  if (!u) return null;
1898
2022
  return { user: u, permission: col.permission };
1899
- }).filter(
1900
- (x) => Boolean(x)
1901
- ).sort((a, b) => a.user.login.localeCompare(b.user.login));
2023
+ }).filter((x) => Boolean(x)).sort((a, b) => a.user.login.localeCompare(b.user.login));
1902
2024
  const { page, per_page } = parsePagination(c);
1903
2025
  const total = users.length;
1904
2026
  const start = (page - 1) * per_page;
@@ -2019,9 +2141,7 @@ function reposRoutes({ app, store, webhooks, baseUrl }) {
2019
2141
  const repo = lookupRepo(gh, owner, repoName);
2020
2142
  if (!repo) throw notFound();
2021
2143
  assertRepoRead(gh, c.get("authUser"), repo);
2022
- const tags = [...gh.tags.findBy("repo_id", repo.id)].sort(
2023
- (a, b) => a.tag.localeCompare(b.tag)
2024
- );
2144
+ const tags = [...gh.tags.findBy("repo_id", repo.id)].sort((a, b) => a.tag.localeCompare(b.tag));
2025
2145
  const { page, per_page } = parsePagination(c);
2026
2146
  const total = tags.length;
2027
2147
  const start = (page - 1) * per_page;
@@ -2931,9 +3051,7 @@ function checkMergeRequirements(gh, pr) {
2931
3051
  if (checks && checks.contexts.length > 0) {
2932
3052
  const runs = gh.checkRuns.findBy("repo_id", baseRepo.id).filter((r) => r.head_sha === pr.head_sha);
2933
3053
  for (const ctx of checks.contexts) {
2934
- const ok = runs.some(
2935
- (r) => r.name === ctx && r.status === "completed" && r.conclusion === "success"
2936
- );
3054
+ const ok = runs.some((r) => r.name === ctx && r.status === "completed" && r.conclusion === "success");
2937
3055
  if (!ok) {
2938
3056
  throw new ApiError(422, "Required status checks have not succeeded.");
2939
3057
  }
@@ -3367,9 +3485,7 @@ ${commitMessage}` : commitTitle;
3367
3485
  changes: 1,
3368
3486
  blob_url: `${baseUrl}/${repo.full_name}/blob/${pr.head_sha}/${filename}`,
3369
3487
  raw_url: `${baseUrl}/${repo.full_name}/raw/${pr.head_sha}/${filename}`,
3370
- contents_url: `${baseUrl}/repos/${repo.full_name}/contents/${encodeURIComponent(
3371
- filename
3372
- )}?ref=${pr.head_ref}`,
3488
+ contents_url: `${baseUrl}/repos/${repo.full_name}/contents/${encodeURIComponent(filename)}?ref=${pr.head_ref}`,
3373
3489
  patch: ""
3374
3490
  }))
3375
3491
  );
@@ -4106,7 +4222,7 @@ function reviewsRoutes({ app, store, webhooks, baseUrl }) {
4106
4222
  const pr = findPull3(gh, repo.id, pullNumber);
4107
4223
  if (!pr) throw notFound();
4108
4224
  const { page, per_page } = parsePagination(c);
4109
- let list = gh.reviews.findBy("repo_id", repo.id).filter((r) => r.pull_number === pullNumber);
4225
+ const list = gh.reviews.findBy("repo_id", repo.id).filter((r) => r.pull_number === pullNumber);
4110
4226
  list.sort((a, b) => a.id - b.id);
4111
4227
  const total = list.length;
4112
4228
  setLinkHeader(c, total, page, per_page);
@@ -4301,9 +4417,7 @@ function reviewsRoutes({ app, store, webhooks, baseUrl }) {
4301
4417
  if (!review) throw notFound();
4302
4418
  const { page, per_page } = parsePagination(c);
4303
4419
  const { sort, direction } = parseCommentSort2(c, "asc");
4304
- let list = gh.comments.findBy("repo_id", repo.id).filter(
4305
- (x) => x.comment_type === "review" && x.pull_number === pullNumber && x.review_id === reviewId
4306
- );
4420
+ let list = gh.comments.findBy("repo_id", repo.id).filter((x) => x.comment_type === "review" && x.pull_number === pullNumber && x.review_id === reviewId);
4307
4421
  list = sortComments2(list, sort, direction);
4308
4422
  const total = list.length;
4309
4423
  setLinkHeader(c, total, page, per_page);
@@ -4360,7 +4474,7 @@ function normalizeColor(raw) {
4360
4474
  if (typeof raw !== "string" || !raw.trim()) {
4361
4475
  throw new ApiError(422, "Validation failed");
4362
4476
  }
4363
- let s = raw.trim().replace(/^#/, "");
4477
+ const s = raw.trim().replace(/^#/, "");
4364
4478
  if (!/^[0-9a-fA-F]{6}$/.test(s)) {
4365
4479
  throw new ApiError(422, "Validation failed");
4366
4480
  }
@@ -4446,7 +4560,7 @@ function labelsAndMilestonesRoutes({ app, store, webhooks, baseUrl }) {
4446
4560
  if (!repo) throw notFound();
4447
4561
  assertRepoRead(gh, c.get("authUser"), repo);
4448
4562
  const { page, per_page } = parsePagination(c);
4449
- let list = gh.labels.findBy("repo_id", repo.id).slice();
4563
+ const list = gh.labels.findBy("repo_id", repo.id).slice();
4450
4564
  list.sort((a, b) => a.name.localeCompare(b.name));
4451
4565
  const total = list.length;
4452
4566
  setLinkHeader(c, total, page, per_page);
@@ -4806,7 +4920,7 @@ function labelsAndMilestonesRoutes({ app, store, webhooks, baseUrl }) {
4806
4920
  creator_id: actor.id
4807
4921
  });
4808
4922
  gh.milestones.update(row.id, { node_id: generateNodeId("Milestone", row.id) });
4809
- let m = recalcMilestoneIssueCounts(gh, repo.id, row.id);
4923
+ const m = recalcMilestoneIssueCounts(gh, repo.id, row.id);
4810
4924
  const ownerLogin2 = ownerLoginOf(gh, repo);
4811
4925
  webhooks.dispatch(
4812
4926
  "milestone",
@@ -4963,7 +5077,7 @@ function labelsAndMilestonesRoutes({ app, store, webhooks, baseUrl }) {
4963
5077
  if (i.milestone_id !== ms.id) continue;
4964
5078
  for (const lid of i.label_ids) labelIdSet.add(lid);
4965
5079
  }
4966
- let labels = [...labelIdSet].map((id) => gh.labels.get(id)).filter(Boolean);
5080
+ const labels = [...labelIdSet].map((id) => gh.labels.get(id)).filter(Boolean);
4967
5081
  labels.sort((a, b) => a.name.localeCompare(b.name));
4968
5082
  const total = labels.length;
4969
5083
  setLinkHeader(c, total, page, per_page);
@@ -5446,9 +5560,7 @@ function branchesAndGitRoutes({ app, store, webhooks, baseUrl }) {
5446
5560
  const repo = lookupRepo(gh, owner, repoName);
5447
5561
  if (!repo) throw notFound();
5448
5562
  assertRepoRead(gh, c.get("authUser"), repo);
5449
- let list = [...gh.branches.findBy("repo_id", repo.id)].sort(
5450
- (a, b) => a.name.localeCompare(b.name)
5451
- );
5563
+ let list = [...gh.branches.findBy("repo_id", repo.id)].sort((a, b) => a.name.localeCompare(b.name));
5452
5564
  const prot = c.req.query("protected");
5453
5565
  if (prot === "true") list = list.filter((b) => b.protected);
5454
5566
  else if (prot === "false") list = list.filter((b) => !b.protected);
@@ -5851,12 +5963,7 @@ function branchesAndGitRoutes({ app, store, webhooks, baseUrl }) {
5851
5963
  object: {
5852
5964
  type: tag.object_type,
5853
5965
  sha: tag.object_sha,
5854
- url: objectApiUrl(
5855
- repo,
5856
- baseUrl,
5857
- resolveGitObjectType(gh, repo.id, tag.object_sha),
5858
- tag.object_sha
5859
- )
5966
+ url: objectApiUrl(repo, baseUrl, resolveGitObjectType(gh, repo.id, tag.object_sha), tag.object_sha)
5860
5967
  },
5861
5968
  verification: { verified: false, reason: "unsigned", signature: null, payload: null, verified_at: null }
5862
5969
  });
@@ -5912,12 +6019,7 @@ function branchesAndGitRoutes({ app, store, webhooks, baseUrl }) {
5912
6019
  object: {
5913
6020
  type: saved.object_type,
5914
6021
  sha: saved.object_sha,
5915
- url: objectApiUrl(
5916
- repo,
5917
- baseUrl,
5918
- resolveGitObjectType(gh, repo.id, saved.object_sha),
5919
- saved.object_sha
5920
- )
6022
+ url: objectApiUrl(repo, baseUrl, resolveGitObjectType(gh, repo.id, saved.object_sha), saved.object_sha)
5921
6023
  },
5922
6024
  verification: { verified: false, reason: "unsigned", signature: null, payload: null, verified_at: null }
5923
6025
  },
@@ -6068,10 +6170,7 @@ function orgsAndTeamsRoutes({ app, store, baseUrl }) {
6068
6170
  const gh = getGitHubStore(store);
6069
6171
  app.get("/organizations", (c) => {
6070
6172
  const since = Math.max(0, parseInt(c.req.query("since") ?? "0", 10) || 0);
6071
- const perPage = Math.min(
6072
- 100,
6073
- Math.max(1, parseInt(c.req.query("per_page") ?? "30", 10) || 30)
6074
- );
6173
+ const perPage = Math.min(100, Math.max(1, parseInt(c.req.query("per_page") ?? "30", 10) || 30));
6075
6174
  const ordered = gh.orgs.all().filter((o) => o.id > since).sort((a, b) => a.id - b.id);
6076
6175
  const page = ordered.slice(0, perPage);
6077
6176
  if (page.length === perPage && ordered.length > perPage) {
@@ -6575,9 +6674,7 @@ _Auto-generated release notes (stub)._
6575
6674
  const repo = lookupRepo(gh, owner, repoName);
6576
6675
  if (!repo) throw notFound();
6577
6676
  assertRepoRead(gh, c.get("authUser"), repo);
6578
- const candidates = releasesForRepo(gh, repo.id).filter(
6579
- (r) => !r.draft && !r.prerelease && r.published_at
6580
- );
6677
+ const candidates = releasesForRepo(gh, repo.id).filter((r) => !r.draft && !r.prerelease && r.published_at);
6581
6678
  if (candidates.length === 0) throw notFound();
6582
6679
  candidates.sort((a, b) => {
6583
6680
  const pa = a.published_at ?? a.created_at;
@@ -7792,9 +7889,7 @@ function searchRoutes({ app, store, baseUrl }) {
7792
7889
  (a, b) => order === "desc" ? b.stargazers_count - a.stargazers_count : a.stargazers_count - b.stargazers_count
7793
7890
  );
7794
7891
  } else if (sortRaw === "forks") {
7795
- list.sort(
7796
- (a, b) => order === "desc" ? b.forks_count - a.forks_count : a.forks_count - b.forks_count
7797
- );
7892
+ list.sort((a, b) => order === "desc" ? b.forks_count - a.forks_count : a.forks_count - b.forks_count);
7798
7893
  } else if (sortRaw === "updated") {
7799
7894
  list.sort(
7800
7895
  (a, b) => order === "desc" ? b.updated_at.localeCompare(a.updated_at) : a.updated_at.localeCompare(b.updated_at)
@@ -7846,7 +7941,7 @@ function searchRoutes({ app, store, baseUrl }) {
7846
7941
  if (body.toLowerCase().includes(t)) s += 1;
7847
7942
  return s;
7848
7943
  }
7849
- let sorted = [...hits];
7944
+ const sorted = [...hits];
7850
7945
  if (sortRaw === "created") {
7851
7946
  sorted.sort((a, b) => {
7852
7947
  const ca = a.kind === "issue" ? a.issue.created_at : a.pr.created_at;
@@ -7921,7 +8016,7 @@ function searchRoutes({ app, store, baseUrl }) {
7921
8016
  if (h.o.name?.toLowerCase().includes(text)) s += 1;
7922
8017
  return s;
7923
8018
  }
7924
- let list = [...hits];
8019
+ const list = [...hits];
7925
8020
  if (sortRaw === "followers") {
7926
8021
  list.sort((a, b) => {
7927
8022
  const fa = a.kind === "user" ? a.u.followers : a.o.followers;
@@ -7947,9 +8042,7 @@ function searchRoutes({ app, store, baseUrl }) {
7947
8042
  const total = list.length;
7948
8043
  const slice = list.slice((page - 1) * per_page, (page - 1) * per_page + per_page);
7949
8044
  setLinkHeader(c, total, page, per_page);
7950
- const items = slice.map(
7951
- (h) => h.kind === "user" ? formatUser(h.u, baseUrl) : formatOrgBrief(h.o, baseUrl)
7952
- );
8045
+ const items = slice.map((h) => h.kind === "user" ? formatUser(h.u, baseUrl) : formatOrgBrief(h.o, baseUrl));
7953
8046
  return c.json({
7954
8047
  total_count: total,
7955
8048
  incomplete_results: false,
@@ -8400,7 +8493,13 @@ function formatArtifact(a, repo, gh, baseUrl) {
8400
8493
  digest: null,
8401
8494
  created_at: a.created_at,
8402
8495
  expires_at: a.expires_at,
8403
- workflow_run: run ? { id: run.id, repository_id: repo.id, head_repository_id: repo.id, head_branch: run.head_branch, head_sha: run.head_sha } : null
8496
+ workflow_run: run ? {
8497
+ id: run.id,
8498
+ repository_id: repo.id,
8499
+ head_repository_id: repo.id,
8500
+ head_branch: run.head_branch,
8501
+ head_sha: run.head_sha
8502
+ } : null
8404
8503
  };
8405
8504
  }
8406
8505
  function filterRuns(gh, runs, q) {
@@ -8518,7 +8617,11 @@ function actionsRoutes({ app, store, webhooks, baseUrl }) {
8518
8617
  void webhooks.dispatch(
8519
8618
  "workflow_run",
8520
8619
  "requested",
8521
- { workflow_run: formatWorkflowRun(created, repo, gh, baseUrl), repository: formatRepo(repo, gh, baseUrl), sender: formatUser(actor, baseUrl) },
8620
+ {
8621
+ workflow_run: formatWorkflowRun(created, repo, gh, baseUrl),
8622
+ repository: formatRepo(repo, gh, baseUrl),
8623
+ sender: formatUser(actor, baseUrl)
8624
+ },
8522
8625
  ownerLogin2,
8523
8626
  repo.name
8524
8627
  );
@@ -8674,13 +8777,11 @@ function actionsRoutes({ app, store, webhooks, baseUrl }) {
8674
8777
  const runId = parseInt(c.req.param("run_id"), 10);
8675
8778
  const run = gh.workflowRuns.get(runId);
8676
8779
  if (!run || run.repo_id !== repo.id) throw notFound();
8677
- return c.text(
8678
- `2025-01-01T00:00:00.0000000Z Workflow run ${run.id} logs (stub)
8780
+ return c.text(`2025-01-01T00:00:00.0000000Z Workflow run ${run.id} logs (stub)
8679
8781
  ${run.head_sha}
8680
- `,
8681
- 200,
8682
- { "Content-Type": "text/plain; charset=utf-8" }
8683
- );
8782
+ `, 200, {
8783
+ "Content-Type": "text/plain; charset=utf-8"
8784
+ });
8684
8785
  });
8685
8786
  app.get("/repos/:owner/:repo/actions/runs/:run_id/jobs", (c) => {
8686
8787
  const owner = c.req.param("owner");
@@ -9001,15 +9102,7 @@ function parseConclusion(raw) {
9001
9102
  if (raw === void 0) return void 0;
9002
9103
  if (raw === null) return null;
9003
9104
  if (typeof raw !== "string") throw new ApiError(422, "Invalid conclusion");
9004
- const allowed = /* @__PURE__ */ new Set([
9005
- "success",
9006
- "failure",
9007
- "neutral",
9008
- "cancelled",
9009
- "skipped",
9010
- "timed_out",
9011
- "action_required"
9012
- ]);
9105
+ const allowed = /* @__PURE__ */ new Set(["success", "failure", "neutral", "cancelled", "skipped", "timed_out", "action_required"]);
9013
9106
  if (!allowed.has(raw)) throw new ApiError(422, "Invalid conclusion");
9014
9107
  return raw;
9015
9108
  }
@@ -9393,7 +9486,7 @@ function checksRoutes({ app, store, webhooks, baseUrl }) {
9393
9486
  };
9394
9487
  }
9395
9488
  const nextStatus = patch.status ?? prev.status;
9396
- let nextConclusion = patch.conclusion !== void 0 ? patch.conclusion : prev.conclusion;
9489
+ const nextConclusion = patch.conclusion !== void 0 ? patch.conclusion : prev.conclusion;
9397
9490
  if (patch.head_sha && patch.head_sha !== prev.head_sha) {
9398
9491
  const newSuite = getOrCreateCheckSuite(gh, repo, patch.head_sha, null);
9399
9492
  patch.check_suite_id = newSuite.id;
@@ -9403,7 +9496,7 @@ function checksRoutes({ app, store, webhooks, baseUrl }) {
9403
9496
  throw new ApiError(422, "conclusion is required when status is completed");
9404
9497
  }
9405
9498
  patch.conclusion = nextConclusion;
9406
- let nextCompleted = patch.completed_at !== void 0 ? patch.completed_at : prev.completed_at;
9499
+ const nextCompleted = patch.completed_at !== void 0 ? patch.completed_at : prev.completed_at;
9407
9500
  if (!nextCompleted) {
9408
9501
  patch.completed_at = timestamp();
9409
9502
  }
@@ -9542,7 +9635,13 @@ function rateLimitRoutes({ app }) {
9542
9635
  integration_manifest: { limit: 5e3, remaining: 4999, reset, used: 1, resource: "integration_manifest" },
9543
9636
  source_import: { limit: 100, remaining: 99, reset, used: 1, resource: "source_import" },
9544
9637
  code_scanning_upload: { limit: 500, remaining: 499, reset, used: 1, resource: "code_scanning_upload" },
9545
- actions_runner_registration: { limit: 1e4, remaining: 9999, reset, used: 1, resource: "actions_runner_registration" },
9638
+ actions_runner_registration: {
9639
+ limit: 1e4,
9640
+ remaining: 9999,
9641
+ reset,
9642
+ used: 1,
9643
+ resource: "actions_runner_registration"
9644
+ },
9546
9645
  scim: { limit: 15e3, remaining: 14999, reset, used: 1, resource: "scim" }
9547
9646
  },
9548
9647
  rate: rateLimit
@@ -9620,13 +9719,13 @@ function metaRoutes({ app, baseUrl }) {
9620
9719
  "+1": `${baseUrl}/emojis/+1.png`,
9621
9720
  "-1": `${baseUrl}/emojis/-1.png`,
9622
9721
  "100": `${baseUrl}/emojis/100.png`,
9623
- "tada": `${baseUrl}/emojis/tada.png`,
9624
- "rocket": `${baseUrl}/emojis/rocket.png`,
9625
- "heart": `${baseUrl}/emojis/heart.png`,
9626
- "eyes": `${baseUrl}/emojis/eyes.png`,
9627
- "thinking": `${baseUrl}/emojis/thinking.png`,
9628
- "thumbsup": `${baseUrl}/emojis/thumbsup.png`,
9629
- "thumbsdown": `${baseUrl}/emojis/thumbsdown.png`
9722
+ tada: `${baseUrl}/emojis/tada.png`,
9723
+ rocket: `${baseUrl}/emojis/rocket.png`,
9724
+ heart: `${baseUrl}/emojis/heart.png`,
9725
+ eyes: `${baseUrl}/emojis/eyes.png`,
9726
+ thinking: `${baseUrl}/emojis/thinking.png`,
9727
+ thumbsup: `${baseUrl}/emojis/thumbsup.png`,
9728
+ thumbsdown: `${baseUrl}/emojis/thumbsdown.png`
9630
9729
  });
9631
9730
  });
9632
9731
  app.get("/zen", (c) => {
@@ -9726,9 +9825,15 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
9726
9825
  );
9727
9826
  }
9728
9827
  if (redirect_uri && !matchesRedirectUri(redirect_uri, oauthApp.redirect_uris)) {
9729
- console.warn(`[OAuth] redirect_uri mismatch: got "${redirect_uri}", registered: ${JSON.stringify(oauthApp.redirect_uris)}`);
9828
+ console.warn(
9829
+ `[OAuth] redirect_uri mismatch: got "${redirect_uri}", registered: ${JSON.stringify(oauthApp.redirect_uris)}`
9830
+ );
9730
9831
  return c.html(
9731
- renderErrorPage("Redirect URI mismatch", "The redirect_uri is not registered for this application.", SERVICE_LABEL),
9832
+ renderErrorPage(
9833
+ "Redirect URI mismatch",
9834
+ "The redirect_uri is not registered for this application.",
9835
+ SERVICE_LABEL
9836
+ ),
9732
9837
  400
9733
9838
  );
9734
9839
  }
@@ -9772,7 +9877,10 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
9772
9877
  clientId: client_id,
9773
9878
  created_at: Date.now()
9774
9879
  });
9775
- debug("github.oauth", `[OAuth callback] generated code: ${code.slice(0, 8)}... for login=${login}, pendingCodes size: ${getPendingCodes(store).size}`);
9880
+ debug(
9881
+ "github.oauth",
9882
+ `[OAuth callback] generated code: ${code.slice(0, 8)}... for login=${login}, pendingCodes size: ${getPendingCodes(store).size}`
9883
+ );
9776
9884
  const sessionId = randomBytes2(24).toString("base64url");
9777
9885
  getSessionMap(store).set(sessionId, login);
9778
9886
  c.header("Set-Cookie", `_emu_session=${sessionId}; Path=/; HttpOnly; SameSite=Lax`);
@@ -9787,7 +9895,10 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
9787
9895
  debug("github.oauth", `[OAuth token] Content-Type: ${contentType}`);
9788
9896
  debug("github.oauth", `[OAuth token] Accept: ${accept}`);
9789
9897
  debug("github.oauth", `[OAuth token] pendingCodes size: ${getPendingCodes(store).size}`);
9790
- debug("github.oauth", `[OAuth token] pendingCodes keys: ${[...getPendingCodes(store).keys()].map((k) => k.slice(0, 8) + "...").join(", ")}`);
9898
+ debug(
9899
+ "github.oauth",
9900
+ `[OAuth token] pendingCodes keys: ${[...getPendingCodes(store).keys()].map((k) => k.slice(0, 8) + "...").join(", ")}`
9901
+ );
9791
9902
  const rawText = await c.req.text();
9792
9903
  debug("github.oauth", `[OAuth token] raw body: ${rawText.slice(0, 500)}`);
9793
9904
  let raw;
@@ -9858,14 +9969,10 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
9858
9969
  }
9859
9970
  const oauthApp = gh.oauthApps.findOneBy("client_id", pending.clientId);
9860
9971
  if (oauthApp) {
9861
- const existingGrant = gh.oauthGrants.all().find(
9862
- (g) => g.user_id === user.id && g.client_id === pending.clientId
9863
- );
9972
+ const existingGrant = gh.oauthGrants.all().find((g) => g.user_id === user.id && g.client_id === pending.clientId);
9864
9973
  const orgAccess = {};
9865
9974
  for (const org of gh.orgs.all()) {
9866
- const isMember = gh.teamMembers.all().some(
9867
- (tm) => tm.user_id === user.id && gh.teams.get(tm.team_id)?.org_id === org.id
9868
- );
9975
+ const isMember = gh.teamMembers.all().some((tm) => tm.user_id === user.id && gh.teams.get(tm.team_id)?.org_id === org.id);
9869
9976
  if (isMember) orgAccess[org.login] = "granted";
9870
9977
  }
9871
9978
  if (existingGrant) {
@@ -9915,19 +10022,19 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
9915
10022
  ]);
9916
10023
  });
9917
10024
  const SCOPE_LABELS = {
9918
- "repo": "Full control of private repositories",
10025
+ repo: "Full control of private repositories",
9919
10026
  "read:user": "Read all user profile data",
9920
10027
  "user:email": "Access user email addresses (read-only)",
9921
- "user": "Full control of user profile",
9922
- "workflow": "Update GitHub action workflows",
10028
+ user: "Full control of user profile",
10029
+ workflow: "Update GitHub action workflows",
9923
10030
  "admin:org": "Full control of orgs and teams",
9924
10031
  "admin:repo_hook": "Full control of repository hooks",
9925
10032
  "read:org": "Read org and team membership",
9926
10033
  "write:repo_hook": "Write repository hooks",
9927
10034
  "read:repo_hook": "Read repository hooks",
9928
- "delete_repo": "Delete repositories",
9929
- "gist": "Create gists",
9930
- "notifications": "Access notifications",
10035
+ delete_repo: "Delete repositories",
10036
+ gist: "Create gists",
10037
+ notifications: "Access notifications",
9931
10038
  "write:packages": "Upload packages",
9932
10039
  "read:packages": "Download packages",
9933
10040
  "admin:gpg_key": "Full control of GPG keys",
@@ -9941,7 +10048,10 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
9941
10048
  app.get("/settings/applications", (c) => {
9942
10049
  const sessionUser = resolveSessionUser(c);
9943
10050
  if (!sessionUser) {
9944
- return c.html(renderErrorPage("Unauthorized", "You must be authenticated to view this page.", SERVICE_LABEL), 401);
10051
+ return c.html(
10052
+ renderErrorPage("Unauthorized", "You must be authenticated to view this page.", SERVICE_LABEL),
10053
+ 401
10054
+ );
9945
10055
  }
9946
10056
  const grants = gh.oauthGrants.findBy("user_id", sessionUser.id);
9947
10057
  let bodyHtml;
@@ -9974,12 +10084,13 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
9974
10084
  app.get("/settings/connections/applications/:client_id", (c) => {
9975
10085
  const sessionUser = resolveSessionUser(c);
9976
10086
  if (!sessionUser) {
9977
- return c.html(renderErrorPage("Unauthorized", "You must be authenticated to view this page.", SERVICE_LABEL), 401);
10087
+ return c.html(
10088
+ renderErrorPage("Unauthorized", "You must be authenticated to view this page.", SERVICE_LABEL),
10089
+ 401
10090
+ );
9978
10091
  }
9979
10092
  const clientId = c.req.param("client_id");
9980
- const grant = gh.oauthGrants.all().find(
9981
- (g) => g.user_id === sessionUser.id && g.client_id === clientId
9982
- );
10093
+ const grant = gh.oauthGrants.all().find((g) => g.user_id === sessionUser.id && g.client_id === clientId);
9983
10094
  if (!grant) {
9984
10095
  return c.html(renderErrorPage("Not Found", "No authorization found for this application.", SERVICE_LABEL), 404);
9985
10096
  }
@@ -9991,9 +10102,7 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
9991
10102
  month: "long",
9992
10103
  day: "numeric"
9993
10104
  });
9994
- const permRows = grant.scopes.map(
9995
- (s) => `<li><span class="check">&#10003;</span> ${escapeHtml(scopeLabel(s))}</li>`
9996
- ).join("\n");
10105
+ const permRows = grant.scopes.map((s) => `<li><span class="check">&#10003;</span> ${escapeHtml(scopeLabel(s))}</li>`).join("\n");
9997
10106
  const orgRows = Object.entries(grant.org_access).map(([org, status]) => {
9998
10107
  const letter = escapeHtml((org[0] ?? "?").toUpperCase());
9999
10108
  const badgeClass = status === "granted" ? "badge-granted" : status === "denied" ? "badge-denied" : "badge-requested";
@@ -10037,12 +10146,13 @@ function oauthRoutes({ app, store, baseUrl, tokenMap }) {
10037
10146
  app.post("/settings/connections/applications/:client_id/revoke", (c) => {
10038
10147
  const sessionUser = resolveSessionUser(c);
10039
10148
  if (!sessionUser) {
10040
- return c.html(renderErrorPage("Unauthorized", "You must be authenticated to perform this action.", SERVICE_LABEL), 401);
10149
+ return c.html(
10150
+ renderErrorPage("Unauthorized", "You must be authenticated to perform this action.", SERVICE_LABEL),
10151
+ 401
10152
+ );
10041
10153
  }
10042
10154
  const clientId = c.req.param("client_id");
10043
- const grant = gh.oauthGrants.all().find(
10044
- (g) => g.user_id === sessionUser.id && g.client_id === clientId
10045
- );
10155
+ const grant = gh.oauthGrants.all().find((g) => g.user_id === sessionUser.id && g.client_id === clientId);
10046
10156
  if (grant) {
10047
10157
  gh.oauthGrants.delete(grant.id);
10048
10158
  }
@@ -10073,10 +10183,13 @@ function appsRoutes({ app, store, baseUrl, tokenMap }) {
10073
10183
  app.get("/app", (c) => {
10074
10184
  const authApp = requireApp(c);
10075
10185
  if (!authApp) {
10076
- return c.json({
10077
- message: "A JSON web token could not be decoded",
10078
- documentation_url: "https://docs.github.com/rest"
10079
- }, 401);
10186
+ return c.json(
10187
+ {
10188
+ message: "A JSON web token could not be decoded",
10189
+ documentation_url: "https://docs.github.com/rest"
10190
+ },
10191
+ 401
10192
+ );
10080
10193
  }
10081
10194
  const ghApp = gh.apps.all().find((a) => a.app_id === authApp.appId);
10082
10195
  if (!ghApp) {
@@ -10102,29 +10215,31 @@ function appsRoutes({ app, store, baseUrl, tokenMap }) {
10102
10215
  app.get("/app/installations", (c) => {
10103
10216
  const authApp = requireApp(c);
10104
10217
  if (!authApp) {
10105
- return c.json({
10106
- message: "A JSON web token could not be decoded",
10107
- documentation_url: "https://docs.github.com/rest"
10108
- }, 401);
10218
+ return c.json(
10219
+ {
10220
+ message: "A JSON web token could not be decoded",
10221
+ documentation_url: "https://docs.github.com/rest"
10222
+ },
10223
+ 401
10224
+ );
10109
10225
  }
10110
10226
  const installations = gh.appInstallations.findBy("app_id", authApp.appId);
10111
10227
  const ghApp = gh.apps.all().find((a) => a.app_id === authApp.appId);
10112
- return c.json(
10113
- installations.map((inst) => formatInstallation(inst, ghApp, baseUrl))
10114
- );
10228
+ return c.json(installations.map((inst) => formatInstallation(inst, ghApp, baseUrl)));
10115
10229
  });
10116
10230
  app.get("/app/installations/:installation_id", (c) => {
10117
10231
  const authApp = requireApp(c);
10118
10232
  if (!authApp) {
10119
- return c.json({
10120
- message: "A JSON web token could not be decoded",
10121
- documentation_url: "https://docs.github.com/rest"
10122
- }, 401);
10233
+ return c.json(
10234
+ {
10235
+ message: "A JSON web token could not be decoded",
10236
+ documentation_url: "https://docs.github.com/rest"
10237
+ },
10238
+ 401
10239
+ );
10123
10240
  }
10124
10241
  const installationId = parseInt(c.req.param("installation_id"), 10);
10125
- const inst = gh.appInstallations.all().find(
10126
- (i) => i.installation_id === installationId && i.app_id === authApp.appId
10127
- );
10242
+ const inst = gh.appInstallations.all().find((i) => i.installation_id === installationId && i.app_id === authApp.appId);
10128
10243
  if (!inst) {
10129
10244
  return c.json({ message: "Not Found", documentation_url: "https://docs.github.com/rest" }, 404);
10130
10245
  }
@@ -10134,15 +10249,16 @@ function appsRoutes({ app, store, baseUrl, tokenMap }) {
10134
10249
  app.post("/app/installations/:installation_id/access_tokens", async (c) => {
10135
10250
  const authApp = requireApp(c);
10136
10251
  if (!authApp) {
10137
- return c.json({
10138
- message: "A JSON web token could not be decoded",
10139
- documentation_url: "https://docs.github.com/rest"
10140
- }, 401);
10252
+ return c.json(
10253
+ {
10254
+ message: "A JSON web token could not be decoded",
10255
+ documentation_url: "https://docs.github.com/rest"
10256
+ },
10257
+ 401
10258
+ );
10141
10259
  }
10142
10260
  const installationId = parseInt(c.req.param("installation_id"), 10);
10143
- const inst = gh.appInstallations.all().find(
10144
- (i) => i.installation_id === installationId && i.app_id === authApp.appId
10145
- );
10261
+ const inst = gh.appInstallations.all().find((i) => i.installation_id === installationId && i.app_id === authApp.appId);
10146
10262
  if (!inst) {
10147
10263
  return c.json({ message: "Not Found", documentation_url: "https://docs.github.com/rest" }, 404);
10148
10264
  }
@@ -10176,13 +10292,16 @@ function appsRoutes({ app, store, baseUrl, tokenMap }) {
10176
10292
  full_name: r.full_name,
10177
10293
  private: r.private
10178
10294
  }));
10179
- return c.json({
10180
- token,
10181
- expires_at: expiresAt,
10182
- permissions: requestedPermissions,
10183
- repository_selection: inst.repository_selection,
10184
- ...inst.repository_selection === "selected" ? { repositories: repos } : {}
10185
- }, 201);
10295
+ return c.json(
10296
+ {
10297
+ token,
10298
+ expires_at: expiresAt,
10299
+ permissions: requestedPermissions,
10300
+ repository_selection: inst.repository_selection,
10301
+ ...inst.repository_selection === "selected" ? { repositories: repos } : {}
10302
+ },
10303
+ 201
10304
+ );
10186
10305
  });
10187
10306
  app.get("/repos/:owner/:repo/installation", (c) => {
10188
10307
  const owner = c.req.param("owner");
@@ -10211,9 +10330,7 @@ function appsRoutes({ app, store, baseUrl, tokenMap }) {
10211
10330
  if (!org) {
10212
10331
  return c.json({ message: "Not Found", documentation_url: "https://docs.github.com/rest" }, 404);
10213
10332
  }
10214
- const inst = gh.appInstallations.all().find(
10215
- (i) => i.account_id === org.id && i.account_type === "Organization"
10216
- );
10333
+ const inst = gh.appInstallations.all().find((i) => i.account_id === org.id && i.account_type === "Organization");
10217
10334
  if (!inst) {
10218
10335
  return c.json({ message: "Not Found", documentation_url: "https://docs.github.com/rest" }, 404);
10219
10336
  }
@@ -10226,9 +10343,7 @@ function appsRoutes({ app, store, baseUrl, tokenMap }) {
10226
10343
  if (!user) {
10227
10344
  return c.json({ message: "Not Found", documentation_url: "https://docs.github.com/rest" }, 404);
10228
10345
  }
10229
- const inst = gh.appInstallations.all().find(
10230
- (i) => i.account_id === user.id && i.account_type === "User"
10231
- );
10346
+ const inst = gh.appInstallations.all().find((i) => i.account_id === user.id && i.account_type === "User");
10232
10347
  if (!inst) {
10233
10348
  return c.json({ message: "Not Found", documentation_url: "https://docs.github.com/rest" }, 404);
10234
10349
  }