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