@pome-sh/cli 0.21.12 → 0.21.14

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.
@@ -1,6 +1,7 @@
1
1
  import './chunk-FKZZWWYC.js';
2
2
  import { defaultSeedState, parseSeed } from './chunk-SGDUD7KK.js';
3
3
  export { defaultSeedState, parseSeed, seedSchema } from './chunk-SGDUD7KK.js';
4
+ import { integerInput, declareRouteInputs, mountDeclaredRoute, UndeclaredInputError, MalformedBodyError } from './chunk-4MQULI7E.js';
4
5
  import { loadMcpToolFixture, defineTwin, twinBuildInfo, deriveMcpToolTable, UnknownToolError, openTwinDatabase, createApp } from './chunk-TV5S6WQV.js';
5
6
  import './chunk-VBATFCWR.js';
6
7
  import './chunk-SG6ZTIMT.js';
@@ -1569,7 +1570,7 @@ function issueCommentJson(comment, repo, target = "issue") {
1569
1570
  updated_at: comment.updated_at
1570
1571
  };
1571
1572
  }
1572
- function pullRequestSimpleJson(pr, baseRepo, headRepo, headSha = null, baseSha = null) {
1573
+ function pullRequestSimpleJson(pr, baseRepo, headRepo, headSha = null, baseSha = null, stack = null) {
1573
1574
  return {
1574
1575
  id: stableNumericId(`${baseRepo.full_name}/pull/${pr.number}`),
1575
1576
  node_id: `PR_${stableNumericId(`${baseRepo.full_name}/pull/${pr.number}`)}`,
@@ -1611,14 +1612,15 @@ function pullRequestSimpleJson(pr, baseRepo, headRepo, headSha = null, baseSha =
1611
1612
  created_at: pr.created_at,
1612
1613
  updated_at: pr.updated_at,
1613
1614
  closed_at: pr.closed_at,
1614
- merged_at: pr.merged_at
1615
+ merged_at: pr.merged_at,
1616
+ stack
1615
1617
  };
1616
1618
  }
1617
- function pullRequestListJson(pr, baseRepo, headRepo, headSha = null, baseSha = null) {
1618
- return pullRequestSimpleJson(pr, baseRepo, headRepo, headSha, baseSha);
1619
+ function pullRequestListJson(pr, baseRepo, headRepo, headSha = null, baseSha = null, stack = null) {
1620
+ return pullRequestSimpleJson(pr, baseRepo, headRepo, headSha, baseSha, stack);
1619
1621
  }
1620
- function pullRequestJson(pr, baseRepo, headRepo, commits = 1, changedFiles = 0, headSha = null, baseSha = null) {
1621
- const simple = pullRequestSimpleJson(pr, baseRepo, headRepo, headSha, baseSha);
1622
+ function pullRequestJson(pr, baseRepo, headRepo, commits = 1, changedFiles = 0, headSha = null, baseSha = null, stack = null) {
1623
+ const simple = pullRequestSimpleJson(pr, baseRepo, headRepo, headSha, baseSha, stack);
1622
1624
  return {
1623
1625
  ...simple,
1624
1626
  merged: Boolean(pr.merged),
@@ -2488,10 +2490,11 @@ function createPullRequest(domain, input, onDelta) {
2488
2490
  }
2489
2491
  function listPullRequests(domain, input) {
2490
2492
  const repo = domain.requireRepo(input.owner, input.repo);
2491
- let rows = domain.listPullRequestRows(repo.id);
2493
+ const all = domain.listPullRequestRows(repo.id);
2494
+ let rows = all;
2492
2495
  if (input.state && input.state !== "all")
2493
2496
  rows = rows.filter((pr) => pr.state === input.state);
2494
- return paginate(rows, input.page, input.per_page ?? input.perPage).map((pr) => domain.serializePullSimple(pr));
2497
+ return paginate(rows, input.page, input.per_page ?? input.perPage).map((pr) => domain.serializePullSimple(pr, all));
2495
2498
  }
2496
2499
  function getPullRequest(domain, input) {
2497
2500
  const repo = domain.requireRepo(input.owner, input.repo);
@@ -3613,6 +3616,73 @@ var GitHubDomain = class {
3613
3616
  this.db.prepare("INSERT INTO pull_request_files (repo_id, pull_number, filename, status, additions, deletions, changes, blob_url, raw_url, contents_url, patch) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)").run(repoId, pullNumber, file.filename, file.status, file.additions, file.deletions, file.changes, file.blob_url, file.raw_url, file.contents_url, file.patch);
3614
3617
  }
3615
3618
  }
3619
+ // F-1178 — the `stack` member of GitHub's `pull-request` / `pull-request-simple`
3620
+ // schemas. GitHub models a stack as its own entity with its own id and number;
3621
+ // the twin has no stack table, so it reads a stack off the state that DOES
3622
+ // encode one — pull requests linked `base_ref` -> `head_ref`, i.e. a PR whose
3623
+ // base branch is another PR's head branch is stacked on top of it.
3624
+ //
3625
+ // The identity this hands out (`id` / `number`) is shared by every member, so
3626
+ // the whole answer has to be a property of the CHAIN and not of the PR the
3627
+ // walk happened to start from. That is why this resolves the entire connected
3628
+ // component and then rejects anything that is not one unambiguous line:
3629
+ // handing two PRs the same `stack.id` while telling them different `size`s
3630
+ // would be worse than the missing field this ticket set out to fix. A
3631
+ // component that is not a simple path — a fork, a cycle — gets `null` from
3632
+ // EVERY member, which is what the schema's `nullable: true` is for.
3633
+ //
3634
+ // `rows` is every pull request in the repo. Passing it in lets the list
3635
+ // surface read the table once instead of once per serialized PR.
3636
+ pullRequestStack(pr, rows = this.listPullRequestRows(pr.repo_id)) {
3637
+ if (pr.state !== "open")
3638
+ return null;
3639
+ const parentsOf = (row) => rows.filter((other) => other.number !== row.number && other.head_repo_id === row.base_repo_id && other.head_ref === row.base_ref);
3640
+ const childrenOf = (row) => rows.filter((other) => other.number !== row.number && other.base_repo_id === row.head_repo_id && other.base_ref === row.head_ref);
3641
+ const component = /* @__PURE__ */ new Map();
3642
+ const queue = [pr];
3643
+ while (queue.length > 0) {
3644
+ const row = queue.shift();
3645
+ if (component.has(row.number))
3646
+ continue;
3647
+ component.set(row.number, row);
3648
+ for (const linked2 of [...parentsOf(row), ...childrenOf(row)]) {
3649
+ if (!component.has(linked2.number))
3650
+ queue.push(linked2);
3651
+ }
3652
+ }
3653
+ const linked = [...component.values()];
3654
+ if (linked.some((row) => parentsOf(row).length > 1 || childrenOf(row).length > 1))
3655
+ return null;
3656
+ const bottoms = linked.filter((row) => parentsOf(row).length === 0);
3657
+ if (bottoms.length !== 1)
3658
+ return null;
3659
+ const chain = [];
3660
+ for (let row = bottoms[0]; row && chain.length < linked.length; row = childrenOf(row)[0]) {
3661
+ chain.push(row);
3662
+ }
3663
+ if (chain.length !== linked.length)
3664
+ return null;
3665
+ const members = chain.filter((row) => row.state === "open");
3666
+ if (members.length < 2)
3667
+ return null;
3668
+ const bottom = members[0];
3669
+ const bottomBaseRepo = this.requireRepoById(bottom.base_repo_id);
3670
+ const baseSha = this.getBranch(bottomBaseRepo.id, bottom.base_ref)?.head_sha ?? bottom.base_sha;
3671
+ if (baseSha === null)
3672
+ return null;
3673
+ return {
3674
+ base: { ref: bottom.base_ref, sha: baseSha },
3675
+ size: members.length,
3676
+ position: members.findIndex((row) => row.number === pr.number) + 1,
3677
+ // The twin has no stack row, so it has no stack id or number of its own.
3678
+ // Both derive from the bottom member, which every other member of the same
3679
+ // chain resolves to identically — that is what makes the identity agree
3680
+ // across the stack, and it is why the linearity check above has to reject
3681
+ // anything whose bottom member depends on where you started.
3682
+ id: stableNumericId(`${bottomBaseRepo.full_name}/stack/${bottom.number}`),
3683
+ number: bottom.number
3684
+ };
3685
+ }
3616
3686
  // Single-PR GET (full PullRequest shape: keeps merged / commits / additions /
3617
3687
  // deletions / changed_files).
3618
3688
  serializePull(pr) {
@@ -3621,16 +3691,18 @@ var GitHubDomain = class {
3621
3691
  const files = this.db.prepare("SELECT * FROM pull_request_files WHERE repo_id = ? AND pull_number = ?").all(pr.repo_id, pr.number);
3622
3692
  const headSha = this.getBranch(headRepo.id, pr.head_ref)?.head_sha ?? pr.head_sha;
3623
3693
  const baseSha = this.getBranch(baseRepo.id, pr.base_ref)?.head_sha ?? pr.base_sha;
3624
- return pullRequestJson(pr, baseRepo, headRepo, 1, files.length, headSha, baseSha);
3694
+ return pullRequestJson(pr, baseRepo, headRepo, 1, files.length, headSha, baseSha, this.pullRequestStack(pr));
3625
3695
  }
3626
3696
  // LIST endpoint (leaner PullRequestSimple shape: drops the single-PR-only
3627
- // diff-stat fields, matching real GitHub).
3628
- serializePullSimple(pr) {
3697
+ // diff-stat fields, matching real GitHub). `rows` is the caller's already-read
3698
+ // repo-wide pull request list, so serializing a page of N does not re-read the
3699
+ // table N times.
3700
+ serializePullSimple(pr, rows) {
3629
3701
  const baseRepo = this.requireRepoById(pr.base_repo_id);
3630
3702
  const headRepo = this.requireRepoById(pr.head_repo_id);
3631
3703
  const headSha = this.getBranch(headRepo.id, pr.head_ref)?.head_sha ?? pr.head_sha;
3632
3704
  const baseSha = this.getBranch(baseRepo.id, pr.base_ref)?.head_sha ?? pr.base_sha;
3633
- return pullRequestListJson(pr, baseRepo, headRepo, headSha, baseSha);
3705
+ return pullRequestListJson(pr, baseRepo, headRepo, headSha, baseSha, this.pullRequestStack(pr, rows));
3634
3706
  }
3635
3707
  resolveHeadRef(baseRepo, head) {
3636
3708
  const [owner, ref] = head.includes(":") ? head.split(":", 2) : [baseRepo.owner, head];
@@ -3780,6 +3852,550 @@ var GitHubDomain = class {
3780
3852
  this.db.prepare("INSERT INTO audit_log (ts, action, repo_full_name, payload_json) VALUES (?, ?, ?, ?)").run(nowIso(), action, repoFullName, JSON.stringify(payload));
3781
3853
  }
3782
3854
  };
3855
+ var repoParams = { owner: z.string().min(1), repo: z.string().min(1) };
3856
+ var numberParam = integerInput({ min: 1 });
3857
+ var pageQuery = {
3858
+ page: integerInput({ min: 1 }).optional(),
3859
+ per_page: integerInput({ min: 1 }).optional()
3860
+ };
3861
+ var stateFilter = z.enum(["open", "closed", "all"]).optional();
3862
+ var stateWrite = z.enum(["open", "closed"]).optional();
3863
+ var repositoryBody = {
3864
+ name: z.string().min(1),
3865
+ description: z.string().optional(),
3866
+ private: z.boolean().optional()
3867
+ };
3868
+ var commentBody = { body: z.string().min(1) };
3869
+ var GITHUB_ROUTES = {
3870
+ // ----- search -----
3871
+ searchRepositories: declareRouteInputs({
3872
+ method: "GET",
3873
+ path: "/search/repositories",
3874
+ query: { q: z.string().optional(), ...pageQuery }
3875
+ }),
3876
+ searchCode: declareRouteInputs({
3877
+ method: "GET",
3878
+ path: "/search/code",
3879
+ query: {
3880
+ q: z.string().optional(),
3881
+ owner: z.string().optional(),
3882
+ repo: z.string().optional(),
3883
+ ...pageQuery
3884
+ }
3885
+ }),
3886
+ searchIssues: declareRouteInputs({
3887
+ method: "GET",
3888
+ path: "/search/issues",
3889
+ query: {
3890
+ q: z.string().optional(),
3891
+ owner: z.string().optional(),
3892
+ repo: z.string().optional(),
3893
+ state: stateFilter,
3894
+ ...pageQuery
3895
+ }
3896
+ }),
3897
+ searchUsers: declareRouteInputs({
3898
+ method: "GET",
3899
+ path: "/search/users",
3900
+ query: { q: z.string().optional(), ...pageQuery }
3901
+ }),
3902
+ searchCommits: declareRouteInputs({
3903
+ method: "GET",
3904
+ path: "/search/commits",
3905
+ query: {
3906
+ q: z.string().optional(),
3907
+ owner: z.string().optional(),
3908
+ repo: z.string().optional(),
3909
+ ...pageQuery
3910
+ }
3911
+ }),
3912
+ // ----- repositories -----
3913
+ getRepository: declareRouteInputs({
3914
+ method: "GET",
3915
+ path: "/repos/:owner/:repo",
3916
+ pathParams: { ...repoParams }
3917
+ }),
3918
+ createUserRepository: declareRouteInputs({
3919
+ method: "POST",
3920
+ path: "/user/repos",
3921
+ bodyEncoding: "json",
3922
+ body: { ...repositoryBody, owner: z.string().min(1).optional() }
3923
+ }),
3924
+ // `owner` is declared in BOTH locations, because the twin accepts it in both.
3925
+ //
3926
+ // The handler spreads the body schema and then overwrites `owner` with the
3927
+ // path value, so the body copy is read and discarded. Declaring only the path
3928
+ // one would turn that into a 422 for a request the twin has always accepted —
3929
+ // a divergence this ticket invented rather than found. The declaration records
3930
+ // what is true: two locations, one of which the handler ignores.
3931
+ createOrgRepository: declareRouteInputs({
3932
+ method: "POST",
3933
+ path: "/orgs/:owner/repos",
3934
+ pathParams: { owner: repoParams.owner },
3935
+ bodyEncoding: "json",
3936
+ body: { ...repositoryBody, owner: z.string().min(1).optional() }
3937
+ }),
3938
+ forkRepository: declareRouteInputs({
3939
+ method: "POST",
3940
+ path: "/repos/:owner/:repo/forks",
3941
+ pathParams: { ...repoParams },
3942
+ bodyEncoding: "json-optional",
3943
+ body: { organization: z.string().optional() }
3944
+ }),
3945
+ // ----- contents & commits -----
3946
+ getRepositoryRootContents: declareRouteInputs({
3947
+ method: "GET",
3948
+ path: "/repos/:owner/:repo/contents",
3949
+ pathParams: { ...repoParams },
3950
+ query: { ref: z.string().optional() }
3951
+ }),
3952
+ getFileContents: declareRouteInputs({
3953
+ method: "GET",
3954
+ path: "/repos/:owner/:repo/contents/*",
3955
+ pathParams: { ...repoParams, path: z.string().min(1) },
3956
+ query: { ref: z.string().optional() }
3957
+ }),
3958
+ createOrUpdateFile: declareRouteInputs({
3959
+ method: "PUT",
3960
+ path: "/repos/:owner/:repo/contents/*",
3961
+ pathParams: { ...repoParams, path: z.string().min(1) },
3962
+ bodyEncoding: "json",
3963
+ body: {
3964
+ message: z.string().min(1),
3965
+ content: z.string(),
3966
+ branch: z.string().optional(),
3967
+ sha: z.string().optional(),
3968
+ encoding: z.enum(["utf-8", "base64"]).optional()
3969
+ }
3970
+ }),
3971
+ listCommits: declareRouteInputs({
3972
+ method: "GET",
3973
+ path: "/repos/:owner/:repo/commits",
3974
+ pathParams: { ...repoParams },
3975
+ query: { sha: z.string().optional(), ...pageQuery }
3976
+ }),
3977
+ createRef: declareRouteInputs({
3978
+ method: "POST",
3979
+ path: "/repos/:owner/:repo/git/refs",
3980
+ pathParams: { ...repoParams },
3981
+ bodyEncoding: "json",
3982
+ body: { ref: z.string().min(1), sha: z.string().optional() }
3983
+ }),
3984
+ // ----- issues -----
3985
+ listIssues: declareRouteInputs({
3986
+ method: "GET",
3987
+ path: "/repos/:owner/:repo/issues",
3988
+ pathParams: { ...repoParams },
3989
+ query: {
3990
+ state: stateFilter,
3991
+ labels: z.string().optional(),
3992
+ assignee: z.string().optional(),
3993
+ ...pageQuery
3994
+ }
3995
+ }),
3996
+ createIssue: declareRouteInputs({
3997
+ method: "POST",
3998
+ path: "/repos/:owner/:repo/issues",
3999
+ pathParams: { ...repoParams },
4000
+ bodyEncoding: "json",
4001
+ body: {
4002
+ title: z.string().min(1),
4003
+ body: z.string().optional(),
4004
+ labels: z.array(z.string()).optional(),
4005
+ assignees: z.array(z.string()).optional()
4006
+ }
4007
+ }),
4008
+ getIssue: declareRouteInputs({
4009
+ method: "GET",
4010
+ path: "/repos/:owner/:repo/issues/:number",
4011
+ pathParams: { ...repoParams, number: numberParam }
4012
+ }),
4013
+ updateIssue: declareRouteInputs({
4014
+ method: "PATCH",
4015
+ path: "/repos/:owner/:repo/issues/:number",
4016
+ pathParams: { ...repoParams, number: numberParam },
4017
+ bodyEncoding: "json",
4018
+ body: {
4019
+ title: z.string().optional(),
4020
+ body: z.string().optional(),
4021
+ state: stateWrite,
4022
+ labels: z.array(z.string()).optional(),
4023
+ assignees: z.array(z.string()).optional()
4024
+ }
4025
+ }),
4026
+ listIssueComments: declareRouteInputs({
4027
+ method: "GET",
4028
+ path: "/repos/:owner/:repo/issues/:number/comments",
4029
+ pathParams: { ...repoParams, number: numberParam },
4030
+ query: { ...pageQuery }
4031
+ }),
4032
+ addIssueComment: declareRouteInputs({
4033
+ method: "POST",
4034
+ path: "/repos/:owner/:repo/issues/:number/comments",
4035
+ pathParams: { ...repoParams, number: numberParam },
4036
+ bodyEncoding: "json",
4037
+ body: { ...commentBody }
4038
+ }),
4039
+ listRepositoryLabels: declareRouteInputs({
4040
+ method: "GET",
4041
+ path: "/repos/:owner/:repo/labels",
4042
+ pathParams: { ...repoParams }
4043
+ }),
4044
+ createRepositoryLabel: declareRouteInputs({
4045
+ method: "POST",
4046
+ path: "/repos/:owner/:repo/labels",
4047
+ pathParams: { ...repoParams },
4048
+ bodyEncoding: "json",
4049
+ body: {
4050
+ name: z.string().min(1),
4051
+ color: z.string().default("ededed"),
4052
+ description: z.string().default("")
4053
+ }
4054
+ }),
4055
+ listIssueLabels: declareRouteInputs({
4056
+ method: "GET",
4057
+ path: "/repos/:owner/:repo/issues/:number/labels",
4058
+ pathParams: { ...repoParams, number: numberParam }
4059
+ }),
4060
+ addIssueLabels: declareRouteInputs({
4061
+ method: "POST",
4062
+ path: "/repos/:owner/:repo/issues/:number/labels",
4063
+ pathParams: { ...repoParams, number: numberParam },
4064
+ bodyEncoding: "json",
4065
+ body: { labels: z.array(z.string().min(1)).min(1) }
4066
+ }),
4067
+ deleteIssueLabel: declareRouteInputs({
4068
+ method: "DELETE",
4069
+ path: "/repos/:owner/:repo/issues/:number/labels/:name",
4070
+ pathParams: { ...repoParams, number: numberParam, name: z.string().min(1) }
4071
+ }),
4072
+ listCollaborators: declareRouteInputs({
4073
+ method: "GET",
4074
+ path: "/repos/:owner/:repo/collaborators",
4075
+ pathParams: { ...repoParams }
4076
+ }),
4077
+ checkCollaborator: declareRouteInputs({
4078
+ method: "GET",
4079
+ path: "/repos/:owner/:repo/collaborators/:username",
4080
+ pathParams: { ...repoParams, username: z.string().min(1) }
4081
+ }),
4082
+ addAssignees: declareRouteInputs({
4083
+ method: "POST",
4084
+ path: "/repos/:owner/:repo/issues/:number/assignees",
4085
+ pathParams: { ...repoParams, number: numberParam },
4086
+ bodyEncoding: "json",
4087
+ body: { assignees: z.array(z.string().min(1)).min(1) }
4088
+ }),
4089
+ // ----- pull requests -----
4090
+ listPullRequests: declareRouteInputs({
4091
+ method: "GET",
4092
+ path: "/repos/:owner/:repo/pulls",
4093
+ pathParams: { ...repoParams },
4094
+ query: { state: stateFilter, ...pageQuery }
4095
+ }),
4096
+ createPullRequest: declareRouteInputs({
4097
+ method: "POST",
4098
+ path: "/repos/:owner/:repo/pulls",
4099
+ pathParams: { ...repoParams },
4100
+ bodyEncoding: "json",
4101
+ body: {
4102
+ title: z.string().min(1),
4103
+ body: z.string().optional(),
4104
+ head: z.string().min(1),
4105
+ base: z.string().optional()
4106
+ }
4107
+ }),
4108
+ getPullRequest: declareRouteInputs({
4109
+ method: "GET",
4110
+ path: "/repos/:owner/:repo/pulls/:number",
4111
+ pathParams: { ...repoParams, number: numberParam }
4112
+ }),
4113
+ listPullRequestFiles: declareRouteInputs({
4114
+ method: "GET",
4115
+ path: "/repos/:owner/:repo/pulls/:number/files",
4116
+ pathParams: { ...repoParams, number: numberParam },
4117
+ query: { ...pageQuery }
4118
+ }),
4119
+ listPullRequestReviews: declareRouteInputs({
4120
+ method: "GET",
4121
+ path: "/repos/:owner/:repo/pulls/:number/reviews",
4122
+ pathParams: { ...repoParams, number: numberParam },
4123
+ query: { ...pageQuery }
4124
+ }),
4125
+ createPullRequestReview: declareRouteInputs({
4126
+ method: "POST",
4127
+ path: "/repos/:owner/:repo/pulls/:number/reviews",
4128
+ pathParams: { ...repoParams, number: numberParam },
4129
+ bodyEncoding: "json",
4130
+ body: {
4131
+ event: z.enum(["APPROVE", "REQUEST_CHANGES", "COMMENT"]),
4132
+ body: z.string().optional()
4133
+ }
4134
+ }),
4135
+ listPullRequestComments: declareRouteInputs({
4136
+ method: "GET",
4137
+ path: "/repos/:owner/:repo/pulls/:number/comments",
4138
+ pathParams: { ...repoParams, number: numberParam },
4139
+ query: { ...pageQuery }
4140
+ }),
4141
+ getPullRequestStatus: declareRouteInputs({
4142
+ method: "GET",
4143
+ path: "/repos/:owner/:repo/pulls/:number/status",
4144
+ pathParams: { ...repoParams, number: numberParam }
4145
+ }),
4146
+ mergePullRequest: declareRouteInputs({
4147
+ method: "PUT",
4148
+ path: "/repos/:owner/:repo/pulls/:number/merge",
4149
+ pathParams: { ...repoParams, number: numberParam },
4150
+ bodyEncoding: "json-optional",
4151
+ body: { commit_title: z.string().optional(), commit_message: z.string().optional() }
4152
+ }),
4153
+ updatePullRequestBranch: declareRouteInputs({
4154
+ method: "PUT",
4155
+ path: "/repos/:owner/:repo/pulls/:number/update-branch",
4156
+ pathParams: { ...repoParams, number: numberParam },
4157
+ bodyEncoding: "json-optional",
4158
+ body: { expected_head_sha: z.string().optional() }
4159
+ }),
4160
+ // ----- v2 cluster A — branches & files -----
4161
+ listBranches: declareRouteInputs({
4162
+ method: "GET",
4163
+ path: "/repos/:owner/:repo/branches",
4164
+ pathParams: { ...repoParams },
4165
+ query: { ...pageQuery }
4166
+ }),
4167
+ // `branch` is the wildcard: a branch name may contain `/`, so the tail is the
4168
+ // whole remainder of the path. The mechanism reads and URL-decodes it.
4169
+ getBranch: declareRouteInputs({
4170
+ method: "GET",
4171
+ path: "/repos/:owner/:repo/branches/*",
4172
+ pathParams: { ...repoParams, branch: z.string().min(1) }
4173
+ }),
4174
+ deleteBranch: declareRouteInputs({
4175
+ method: "DELETE",
4176
+ path: "/repos/:owner/:repo/git/refs/heads/*",
4177
+ pathParams: { ...repoParams, branch: z.string().min(1) }
4178
+ }),
4179
+ deleteFile: declareRouteInputs({
4180
+ method: "DELETE",
4181
+ path: "/repos/:owner/:repo/contents/*",
4182
+ pathParams: { ...repoParams, path: z.string().min(1) },
4183
+ bodyEncoding: "json",
4184
+ body: {
4185
+ message: z.string().min(1),
4186
+ sha: z.string().min(1),
4187
+ branch: z.string().optional()
4188
+ }
4189
+ }),
4190
+ // ----- v2 cluster B — commits & diffs -----
4191
+ getCommit: declareRouteInputs({
4192
+ method: "GET",
4193
+ path: "/repos/:owner/:repo/commits/:ref",
4194
+ pathParams: { ...repoParams, ref: z.string().min(1) }
4195
+ }),
4196
+ // `:basehead{.+}` is a NAMED param with a regex tail, not a wildcard; the
4197
+ // handler is what splits `base...head` out of it.
4198
+ compareCommits: declareRouteInputs({
4199
+ method: "GET",
4200
+ path: "/repos/:owner/:repo/compare/:basehead{.+}",
4201
+ pathParams: { ...repoParams, basehead: z.string().min(1) }
4202
+ }),
4203
+ getPullRequestDiff: declareRouteInputs({
4204
+ method: "GET",
4205
+ path: "/repos/:owner/:repo/pulls/:number/diff",
4206
+ pathParams: { ...repoParams, number: numberParam }
4207
+ }),
4208
+ // ----- v2 cluster C — pull requests deeper -----
4209
+ updatePullRequest: declareRouteInputs({
4210
+ method: "PATCH",
4211
+ path: "/repos/:owner/:repo/pulls/:number",
4212
+ pathParams: { ...repoParams, number: numberParam },
4213
+ bodyEncoding: "json",
4214
+ body: {
4215
+ title: z.string().optional(),
4216
+ body: z.string().optional(),
4217
+ state: stateWrite,
4218
+ base: z.string().optional()
4219
+ }
4220
+ }),
4221
+ listPullRequestCommits: declareRouteInputs({
4222
+ method: "GET",
4223
+ path: "/repos/:owner/:repo/pulls/:number/commits",
4224
+ pathParams: { ...repoParams, number: numberParam },
4225
+ query: { ...pageQuery }
4226
+ }),
4227
+ createPullRequestReviewComment: declareRouteInputs({
4228
+ method: "POST",
4229
+ path: "/repos/:owner/:repo/pulls/:number/comments",
4230
+ pathParams: { ...repoParams, number: numberParam },
4231
+ bodyEncoding: "json",
4232
+ body: {
4233
+ body: z.string().min(1),
4234
+ path: z.string().min(1),
4235
+ line: integerInput({ min: 1 }),
4236
+ side: z.enum(["LEFT", "RIGHT"]).optional(),
4237
+ commit_id: z.string().optional()
4238
+ }
4239
+ }),
4240
+ replyToPullRequestReviewComment: declareRouteInputs({
4241
+ method: "POST",
4242
+ path: "/repos/:owner/:repo/pulls/:number/comments/:comment_id/replies",
4243
+ pathParams: { ...repoParams, number: numberParam, comment_id: numberParam },
4244
+ bodyEncoding: "json",
4245
+ body: { ...commentBody }
4246
+ }),
4247
+ // ----- v2 cluster D — issue comments deeper -----
4248
+ updateIssueComment: declareRouteInputs({
4249
+ method: "PATCH",
4250
+ path: "/repos/:owner/:repo/issues/comments/:comment_id",
4251
+ pathParams: { ...repoParams, comment_id: numberParam },
4252
+ bodyEncoding: "json",
4253
+ body: { ...commentBody }
4254
+ }),
4255
+ deleteIssueComment: declareRouteInputs({
4256
+ method: "DELETE",
4257
+ path: "/repos/:owner/:repo/issues/comments/:comment_id",
4258
+ pathParams: { ...repoParams, comment_id: numberParam }
4259
+ }),
4260
+ // ----- v2 cluster E — milestones -----
4261
+ listMilestones: declareRouteInputs({
4262
+ method: "GET",
4263
+ path: "/repos/:owner/:repo/milestones",
4264
+ pathParams: { ...repoParams },
4265
+ query: { state: stateFilter, ...pageQuery }
4266
+ }),
4267
+ createMilestone: declareRouteInputs({
4268
+ method: "POST",
4269
+ path: "/repos/:owner/:repo/milestones",
4270
+ pathParams: { ...repoParams },
4271
+ bodyEncoding: "json",
4272
+ body: {
4273
+ title: z.string().min(1),
4274
+ description: z.string().optional(),
4275
+ due_on: z.string().optional(),
4276
+ state: stateWrite
4277
+ }
4278
+ }),
4279
+ updateMilestone: declareRouteInputs({
4280
+ method: "PATCH",
4281
+ path: "/repos/:owner/:repo/milestones/:number",
4282
+ pathParams: { ...repoParams, number: numberParam },
4283
+ bodyEncoding: "json",
4284
+ body: {
4285
+ title: z.string().optional(),
4286
+ description: z.string().optional(),
4287
+ due_on: z.string().optional(),
4288
+ state: stateWrite
4289
+ }
4290
+ }),
4291
+ deleteMilestone: declareRouteInputs({
4292
+ method: "DELETE",
4293
+ path: "/repos/:owner/:repo/milestones/:number",
4294
+ pathParams: { ...repoParams, number: numberParam }
4295
+ }),
4296
+ // ----- v2 cluster F — commit status + checks -----
4297
+ createCommitStatus: declareRouteInputs({
4298
+ method: "POST",
4299
+ path: "/repos/:owner/:repo/statuses/:sha",
4300
+ pathParams: { ...repoParams, sha: z.string().min(1) },
4301
+ bodyEncoding: "json",
4302
+ body: {
4303
+ state: z.enum(["error", "failure", "pending", "success"]),
4304
+ context: z.string().optional(),
4305
+ description: z.string().optional(),
4306
+ target_url: z.string().optional()
4307
+ }
4308
+ }),
4309
+ getCombinedStatusForRef: declareRouteInputs({
4310
+ method: "GET",
4311
+ path: "/repos/:owner/:repo/commits/:ref/status",
4312
+ pathParams: { ...repoParams, ref: z.string().min(1) }
4313
+ }),
4314
+ createCheckRun: declareRouteInputs({
4315
+ method: "POST",
4316
+ path: "/repos/:owner/:repo/check-runs",
4317
+ pathParams: { ...repoParams },
4318
+ bodyEncoding: "json",
4319
+ body: {
4320
+ name: z.string().min(1),
4321
+ head_sha: z.string().min(1),
4322
+ status: z.enum(["queued", "in_progress", "completed"]).optional(),
4323
+ conclusion: z.enum([
4324
+ "success",
4325
+ "failure",
4326
+ "neutral",
4327
+ "cancelled",
4328
+ "timed_out",
4329
+ "action_required",
4330
+ "skipped",
4331
+ "stale"
4332
+ ]).optional(),
4333
+ details_url: z.string().optional(),
4334
+ external_id: z.string().optional(),
4335
+ output: z.object({ title: z.string().optional(), summary: z.string().optional() }).optional(),
4336
+ started_at: z.string().optional(),
4337
+ completed_at: z.string().optional()
4338
+ }
4339
+ }),
4340
+ listCheckRunsForRef: declareRouteInputs({
4341
+ method: "GET",
4342
+ path: "/repos/:owner/:repo/commits/:ref/check-runs",
4343
+ pathParams: { ...repoParams, ref: z.string().min(1) },
4344
+ query: { ...pageQuery }
4345
+ }),
4346
+ // ----- v2 cluster G — tags & releases -----
4347
+ listTags: declareRouteInputs({
4348
+ method: "GET",
4349
+ path: "/repos/:owner/:repo/tags",
4350
+ pathParams: { ...repoParams },
4351
+ query: { ...pageQuery }
4352
+ }),
4353
+ listReleases: declareRouteInputs({
4354
+ method: "GET",
4355
+ path: "/repos/:owner/:repo/releases",
4356
+ pathParams: { ...repoParams },
4357
+ query: { ...pageQuery }
4358
+ }),
4359
+ getLatestRelease: declareRouteInputs({
4360
+ method: "GET",
4361
+ path: "/repos/:owner/:repo/releases/latest",
4362
+ pathParams: { ...repoParams }
4363
+ }),
4364
+ getReleaseByTag: declareRouteInputs({
4365
+ method: "GET",
4366
+ path: "/repos/:owner/:repo/releases/tags/*",
4367
+ pathParams: { ...repoParams, tag: z.string().min(1) }
4368
+ }),
4369
+ createRelease: declareRouteInputs({
4370
+ method: "POST",
4371
+ path: "/repos/:owner/:repo/releases",
4372
+ pathParams: { ...repoParams },
4373
+ bodyEncoding: "json",
4374
+ body: {
4375
+ tag_name: z.string().min(1),
4376
+ target_commitish: z.string().optional(),
4377
+ name: z.string().optional(),
4378
+ body: z.string().optional(),
4379
+ draft: z.boolean().optional(),
4380
+ prerelease: z.boolean().optional()
4381
+ }
4382
+ }),
4383
+ // ----- v2 cluster H — identity & collaborators -----
4384
+ // The authenticated login comes from the session claim, not from a request
4385
+ // input, so this surface declares nothing.
4386
+ getAuthenticatedUser: declareRouteInputs({ method: "GET", path: "/user" }),
4387
+ addCollaborator: declareRouteInputs({
4388
+ method: "PUT",
4389
+ path: "/repos/:owner/:repo/collaborators/:username",
4390
+ pathParams: { ...repoParams, username: z.string().min(1) },
4391
+ bodyEncoding: "json-optional",
4392
+ body: {
4393
+ permission: z.enum(["pull", "push", "admin", "maintain", "triage"]).optional()
4394
+ }
4395
+ })
4396
+ };
4397
+
4398
+ // ../packages/twin-github/dist/src/routes.js
3783
4399
  function captureDelta(fn) {
3784
4400
  let delta = null;
3785
4401
  const value = fn((d) => {
@@ -3787,272 +4403,218 @@ function captureDelta(fn) {
3787
4403
  });
3788
4404
  return { value, delta };
3789
4405
  }
3790
- var createRepoSchema = z.object({ name: z.string().min(1), owner: z.string().min(1).optional(), description: z.string().optional(), private: z.boolean().optional() });
3791
- var contentSchema = z.object({ message: z.string().min(1), content: z.string(), branch: z.string().optional(), sha: z.string().optional(), encoding: z.enum(["utf-8", "base64"]).optional() });
3792
- var createIssueSchema = z.object({ title: z.string().min(1), body: z.string().optional(), labels: z.array(z.string()).optional(), assignees: z.array(z.string()).optional() });
3793
- var updateIssueSchema = z.object({ title: z.string().optional(), body: z.string().optional(), state: z.enum(["open", "closed"]).optional(), labels: z.array(z.string()).optional(), assignees: z.array(z.string()).optional() });
3794
- var commentSchema = z.object({ body: z.string().min(1) });
3795
- var labelSchema = z.object({ name: z.string().min(1), color: z.string().default("ededed"), description: z.string().default("") });
3796
- var labelsSchema = z.object({ labels: z.array(z.string().min(1)).min(1) });
3797
- var assigneesSchema = z.object({ assignees: z.array(z.string().min(1)).min(1) });
3798
- var createPullSchema = z.object({ title: z.string().min(1), body: z.string().optional(), head: z.string().min(1), base: z.string().optional() });
3799
- var reviewSchema = z.object({ event: z.enum(["APPROVE", "REQUEST_CHANGES", "COMMENT"]), body: z.string().optional() });
3800
- var mergeSchema = z.object({ commit_title: z.string().optional(), commit_message: z.string().optional() });
3801
- var updateBranchSchema = z.object({ expected_head_sha: z.string().optional() });
3802
- var deleteFileSchema = z.object({ message: z.string().min(1), sha: z.string().min(1), branch: z.string().optional() });
3803
- var updatePrSchema = z.object({ title: z.string().optional(), body: z.string().optional(), state: z.enum(["open", "closed"]).optional(), base: z.string().optional() });
3804
- var reviewCommentSchema = z.object({ body: z.string().min(1), path: z.string().min(1), line: z.coerce.number().int().positive(), side: z.enum(["LEFT", "RIGHT"]).optional(), commit_id: z.string().optional() });
3805
- var replyCommentSchema = z.object({ body: z.string().min(1) });
3806
- var updateCommentSchema = z.object({ body: z.string().min(1) });
3807
- var milestoneSchema = z.object({ title: z.string().min(1), description: z.string().optional(), due_on: z.string().optional(), state: z.enum(["open", "closed"]).optional() });
3808
- var updateMilestoneSchema = z.object({ title: z.string().optional(), description: z.string().optional(), due_on: z.string().optional(), state: z.enum(["open", "closed"]).optional() });
3809
- var createStatusSchema = z.object({ state: z.enum(["error", "failure", "pending", "success"]), context: z.string().optional(), description: z.string().optional(), target_url: z.string().optional() });
3810
- var createCheckRunSchema = z.object({
3811
- name: z.string().min(1),
3812
- head_sha: z.string().min(1),
3813
- status: z.enum(["queued", "in_progress", "completed"]).optional(),
3814
- conclusion: z.enum(["success", "failure", "neutral", "cancelled", "timed_out", "action_required", "skipped", "stale"]).optional(),
3815
- details_url: z.string().optional(),
3816
- external_id: z.string().optional(),
3817
- output: z.object({ title: z.string().optional(), summary: z.string().optional() }).optional(),
3818
- started_at: z.string().optional(),
3819
- completed_at: z.string().optional()
3820
- });
3821
- var createReleaseSchema = z.object({
3822
- tag_name: z.string().min(1),
3823
- target_commitish: z.string().optional(),
3824
- name: z.string().optional(),
3825
- body: z.string().optional(),
3826
- draft: z.boolean().optional(),
3827
- prerelease: z.boolean().optional()
3828
- });
3829
- var addCollaboratorSchema = z.object({ permission: z.enum(["pull", "push", "admin", "maintain", "triage"]).optional() });
3830
4406
  function registerGitHubRoutes(session, { domain, recorder }) {
3831
- const handle = (fn) => recorder.handle({ mutation: false }, async (c) => {
3832
- const result = await fn(c);
3833
- return { status: result.status, body: result.body, mutation: result.mutation ?? false, delta: result.delta ?? null };
3834
- });
3835
- const handleAs = (tool, fn) => recorder.handle({ mutation: false, tool }, async (c) => {
3836
- const result = await fn(c);
3837
- return { status: result.status, body: result.body, mutation: result.mutation ?? false, delta: result.delta ?? null };
3838
- });
3839
- session.get("/search/repositories", handle((c) => ok(domain.searchRepositories({ q: c.req.query("q"), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
3840
- session.get("/search/code", handle((c) => ok(domain.searchCode({ q: c.req.query("q"), owner: c.req.query("owner"), repo: c.req.query("repo"), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
3841
- session.get("/search/issues", handle((c) => ok(domain.searchIssues({ q: c.req.query("q"), owner: c.req.query("owner"), repo: c.req.query("repo"), state: stateQuery(c), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
3842
- session.get("/search/users", handle((c) => ok(domain.searchUsers({ q: c.req.query("q"), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
3843
- session.get("/search/commits", handle((c) => ok(domain.searchCommits({ q: c.req.query("q"), owner: c.req.query("owner"), repo: c.req.query("repo"), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
3844
- session.get("/repos/:owner/:repo", handle((c) => ok(domain.getRepository(params(c)))));
3845
- session.post("/user/repos", handle(async (c) => {
3846
- const args = createRepoSchema.parse(await readJson(c));
3847
- const { value, delta } = captureDelta((onDelta) => domain.createRepository(args, onDelta));
4407
+ const route = (declaration, handler, tool) => {
4408
+ mountDeclaredRoute(session, declaration, recorder.handle({ mutation: false, ...tool ? { tool } : {} }, async (c) => {
4409
+ const input = await parseDeclared(declaration, c);
4410
+ const result = await handler(input, c);
4411
+ return {
4412
+ status: result.status,
4413
+ body: result.body,
4414
+ mutation: result.mutation ?? false,
4415
+ delta: result.delta ?? null
4416
+ };
4417
+ }));
4418
+ };
4419
+ route(GITHUB_ROUTES.searchRepositories, ({ query }) => ok(domain.searchRepositories(query)));
4420
+ route(GITHUB_ROUTES.searchCode, ({ query }) => ok(domain.searchCode(query)));
4421
+ route(GITHUB_ROUTES.searchIssues, ({ query }) => ok(domain.searchIssues(query)));
4422
+ route(GITHUB_ROUTES.searchUsers, ({ query }) => ok(domain.searchUsers(query)));
4423
+ route(GITHUB_ROUTES.searchCommits, ({ query }) => ok(domain.searchCommits(query)));
4424
+ route(GITHUB_ROUTES.getRepository, ({ path }) => ok(domain.getRepository(path)));
4425
+ route(GITHUB_ROUTES.createUserRepository, ({ body }) => {
4426
+ const { value, delta } = captureDelta((onDelta) => domain.createRepository(body, onDelta));
3848
4427
  return created(value, delta);
3849
- }));
3850
- session.post("/orgs/:owner/repos", handle(async (c) => {
3851
- const args = { ...createRepoSchema.parse(await readJson(c)), owner: c.req.param("owner") };
3852
- const { value, delta } = captureDelta((onDelta) => domain.createRepository(args, onDelta));
4428
+ });
4429
+ route(GITHUB_ROUTES.createOrgRepository, ({ path, body }) => {
4430
+ const { value, delta } = captureDelta((onDelta) => domain.createRepository({ ...body, owner: path.owner }, onDelta));
3853
4431
  return created(value, delta);
3854
- }));
3855
- session.post("/repos/:owner/:repo/forks", handle(async (c) => {
3856
- const organization = (await maybeJson(c)).organization;
3857
- const { value, delta } = captureDelta((onDelta) => domain.forkRepository({ ...params(c), organization }, onDelta));
4432
+ });
4433
+ route(GITHUB_ROUTES.forkRepository, ({ path, body }) => {
4434
+ const { value, delta } = captureDelta((onDelta) => domain.forkRepository({ ...path, ...body }, onDelta));
3858
4435
  return created(value, delta);
3859
- }));
3860
- session.get("/repos/:owner/:repo/contents", handle((c) => ok(domain.getFileContents({ ...params(c), path: "", ref: c.req.query("ref") }))));
3861
- session.get("/repos/:owner/:repo/contents/*", handle((c) => ok(domain.getFileContents({ ...params(c), path: contentPath(c), ref: c.req.query("ref") }))));
3862
- session.put("/repos/:owner/:repo/contents/*", handle(async (c) => {
3863
- const body = contentSchema.parse(await readJson(c));
3864
- const args = { ...params(c), path: contentPath(c), ...body };
3865
- const actor = sessionLogin(c);
3866
- const { value, delta } = captureDelta((onDelta) => domain.createOrUpdateFile(args, { actor }, onDelta));
4436
+ });
4437
+ route(GITHUB_ROUTES.getRepositoryRootContents, ({ path, query }) => ok(domain.getFileContents({ owner: path.owner, repo: path.repo, path: "", ref: query.ref })));
4438
+ route(GITHUB_ROUTES.getFileContents, ({ path, query }) => ok(domain.getFileContents({ ...path, ref: query.ref })));
4439
+ route(GITHUB_ROUTES.createOrUpdateFile, ({ path, body }, c) => {
4440
+ const { value, delta } = captureDelta((onDelta) => domain.createOrUpdateFile({ ...path, ...body }, { actor: sessionLogin(c) }, onDelta));
3867
4441
  const status = delta !== null && delta.before === null ? 201 : 200;
3868
4442
  return { status, body: value, mutation: true, delta };
3869
- }));
3870
- session.get("/repos/:owner/:repo/commits", handle((c) => ok(domain.listCommits({ ...params(c), sha: c.req.query("sha"), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
3871
- session.post("/repos/:owner/:repo/git/refs", handle(async (c) => {
3872
- const body = z.object({ ref: z.string().min(1), sha: z.string().optional() }).parse(await readJson(c));
4443
+ });
4444
+ route(GITHUB_ROUTES.listCommits, ({ path, query }) => ok(domain.listCommits({ ...path, ...query })));
4445
+ route(GITHUB_ROUTES.createRef, ({ path, body }) => {
3873
4446
  const branch = body.ref.replace(/^refs\/heads\//, "");
3874
- const { value, delta } = captureDelta((onDelta) => domain.createBranch({ ...params(c), branch, sha: body.sha }, onDelta));
4447
+ const { value, delta } = captureDelta((onDelta) => domain.createBranch({ ...path, branch, sha: body.sha }, onDelta));
3875
4448
  return created(value, delta);
3876
- }));
3877
- session.get("/repos/:owner/:repo/issues", handle((c) => ok(domain.listIssues({ ...params(c), state: stateQuery(c), labels: c.req.query("labels"), assignee: c.req.query("assignee"), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
3878
- session.post("/repos/:owner/:repo/issues", handle(async (c) => {
3879
- const args = { ...params(c), ...createIssueSchema.parse(await readJson(c)) };
3880
- const { value, delta } = captureDelta((onDelta) => domain.createIssue(args, onDelta));
4449
+ });
4450
+ route(GITHUB_ROUTES.listIssues, ({ path, query }) => ok(domain.listIssues({ ...path, ...query })));
4451
+ route(GITHUB_ROUTES.createIssue, ({ path, body }) => {
4452
+ const { value, delta } = captureDelta((onDelta) => domain.createIssue({ ...path, ...body }, onDelta));
3881
4453
  return created(value, delta);
3882
- }));
3883
- session.get("/repos/:owner/:repo/issues/:number", handle((c) => ok(domain.getIssue({ ...params(c), issue_number: numberParam(c, "number") }))));
3884
- session.patch("/repos/:owner/:repo/issues/:number", handle(async (c) => {
3885
- const args = { ...params(c), issue_number: numberParam(c, "number"), ...updateIssueSchema.parse(await readJson(c)) };
3886
- const { value, delta } = captureDelta((onDelta) => domain.updateIssue(args, onDelta));
4454
+ });
4455
+ route(GITHUB_ROUTES.getIssue, ({ path }) => ok(domain.getIssue(issueRef(path))));
4456
+ route(GITHUB_ROUTES.updateIssue, ({ path, body }) => {
4457
+ const { value, delta } = captureDelta((onDelta) => domain.updateIssue({ ...issueRef(path), ...body }, onDelta));
3887
4458
  return ok(value, true, delta);
3888
- }));
3889
- session.get("/repos/:owner/:repo/issues/:number/comments", handle((c) => ok(domain.listIssueComments({ ...params(c), issue_number: numberParam(c, "number"), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
3890
- session.post("/repos/:owner/:repo/issues/:number/comments", handle(async (c) => {
3891
- const args = { ...params(c), issue_number: numberParam(c, "number"), ...commentSchema.parse(await readJson(c)) };
3892
- const { value, delta } = captureDelta((onDelta) => domain.addIssueComment(args, onDelta));
4459
+ });
4460
+ route(GITHUB_ROUTES.listIssueComments, ({ path, query }) => ok(domain.listIssueComments({ ...issueRef(path), ...query })));
4461
+ route(GITHUB_ROUTES.addIssueComment, ({ path, body }) => {
4462
+ const { value, delta } = captureDelta((onDelta) => domain.addIssueComment({ ...issueRef(path), ...body }, onDelta));
3893
4463
  return created(value, delta);
3894
- }));
3895
- session.get("/repos/:owner/:repo/labels", handle((c) => ok(domain.listRepositoryLabels(params(c)))));
3896
- session.post("/repos/:owner/:repo/labels", handle(async (c) => {
3897
- const args = { ...params(c), ...labelSchema.parse(await readJson(c)) };
3898
- const { value, delta } = captureDelta((onDelta) => domain.createRepositoryLabel(args, onDelta));
4464
+ });
4465
+ route(GITHUB_ROUTES.listRepositoryLabels, ({ path }) => ok(domain.listRepositoryLabels(path)));
4466
+ route(GITHUB_ROUTES.createRepositoryLabel, ({ path, body }) => {
4467
+ const { value, delta } = captureDelta((onDelta) => domain.createRepositoryLabel({ ...path, ...body }, onDelta));
3899
4468
  return created(value, delta);
3900
- }));
3901
- session.get("/repos/:owner/:repo/issues/:number/labels", handle((c) => ok(domain.listIssueLabelsForIssue({ ...params(c), issue_number: numberParam(c, "number") }))));
3902
- session.post("/repos/:owner/:repo/issues/:number/labels", handle(async (c) => {
3903
- const args = { ...params(c), issue_number: numberParam(c, "number"), ...labelsSchema.parse(await readJson(c)) };
3904
- const { value, delta } = captureDelta((onDelta) => domain.addIssueLabels(args, onDelta));
4469
+ });
4470
+ route(GITHUB_ROUTES.listIssueLabels, ({ path }) => ok(domain.listIssueLabelsForIssue(issueRef(path))));
4471
+ route(GITHUB_ROUTES.addIssueLabels, ({ path, body }) => {
4472
+ const { value, delta } = captureDelta((onDelta) => domain.addIssueLabels({ ...issueRef(path), ...body }, onDelta));
3905
4473
  return ok(value, true, delta);
3906
- }));
3907
- session.delete("/repos/:owner/:repo/issues/:number/labels/:name", handle((c) => {
3908
- const args = { ...params(c), issue_number: numberParam(c, "number"), label: requireParam(c, "name") };
3909
- const { value, delta } = captureDelta((onDelta) => domain.deleteIssueLabel(args, onDelta));
4474
+ });
4475
+ route(GITHUB_ROUTES.deleteIssueLabel, ({ path }) => {
4476
+ const { value, delta } = captureDelta((onDelta) => domain.deleteIssueLabel({ ...issueRef(path), label: path.name }, onDelta));
3910
4477
  return ok(value, true, delta);
3911
- }));
3912
- session.get("/repos/:owner/:repo/collaborators", handle((c) => ok(domain.listCollaborators(params(c)))));
3913
- session.get("/repos/:owner/:repo/collaborators/:username", handle((c) => {
3914
- const found = domain.isCollaborator({ ...params(c), username: requireParam(c, "username") });
3915
- if (!found)
4478
+ });
4479
+ route(GITHUB_ROUTES.listCollaborators, ({ path }) => ok(domain.listCollaborators(path)));
4480
+ route(GITHUB_ROUTES.checkCollaborator, ({ path }) => {
4481
+ if (!domain.isCollaborator(path))
3916
4482
  throw new TwinError("Not Found", 404);
3917
4483
  return { status: 204, body: null, mutation: false };
3918
- }));
3919
- session.post("/repos/:owner/:repo/issues/:number/assignees", handle(async (c) => {
3920
- const args = { ...params(c), issue_number: numberParam(c, "number"), ...assigneesSchema.parse(await readJson(c)) };
3921
- const { value, delta } = captureDelta((onDelta) => domain.addAssignees(args, onDelta));
4484
+ });
4485
+ route(GITHUB_ROUTES.addAssignees, ({ path, body }) => {
4486
+ const { value, delta } = captureDelta((onDelta) => domain.addAssignees({ ...issueRef(path), ...body }, onDelta));
3922
4487
  return created(value, delta);
3923
- }));
3924
- session.get("/repos/:owner/:repo/pulls", handle((c) => ok(domain.listPullRequests({ ...params(c), state: stateQuery(c), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
3925
- session.post("/repos/:owner/:repo/pulls", handle(async (c) => {
3926
- const actor = sessionLogin(c);
3927
- const args = { ...params(c), ...createPullSchema.parse(await readJson(c)), actor };
4488
+ });
4489
+ route(GITHUB_ROUTES.listPullRequests, ({ path, query }) => ok(domain.listPullRequests({ ...path, ...query })));
4490
+ route(GITHUB_ROUTES.createPullRequest, ({ path, body }, c) => {
4491
+ const args = { ...path, ...body, actor: sessionLogin(c) };
3928
4492
  const { value, delta } = captureDelta((onDelta) => domain.createPullRequest(args, onDelta));
3929
4493
  return created(value, delta);
3930
- }));
3931
- session.get("/repos/:owner/:repo/pulls/:number", handle((c) => ok(domain.getPullRequest({ ...params(c), pull_number: numberParam(c, "number") }))));
3932
- session.get("/repos/:owner/:repo/pulls/:number/files", handle((c) => ok(domain.getPullRequestFiles({ ...params(c), pull_number: numberParam(c, "number"), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
3933
- session.get("/repos/:owner/:repo/pulls/:number/reviews", handle((c) => ok(domain.getPullRequestReviews({ ...params(c), pull_number: numberParam(c, "number"), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
3934
- session.post("/repos/:owner/:repo/pulls/:number/reviews", handle(async (c) => {
3935
- const args = { ...params(c), pull_number: numberParam(c, "number"), ...reviewSchema.parse(await readJson(c)) };
3936
- const { value, delta } = captureDelta((onDelta) => domain.createPullRequestReview(args, onDelta));
4494
+ });
4495
+ route(GITHUB_ROUTES.getPullRequest, ({ path }) => ok(domain.getPullRequest(pullRef(path))));
4496
+ route(GITHUB_ROUTES.listPullRequestFiles, ({ path, query }) => ok(domain.getPullRequestFiles({ ...pullRef(path), ...query })));
4497
+ route(GITHUB_ROUTES.listPullRequestReviews, ({ path, query }) => ok(domain.getPullRequestReviews({ ...pullRef(path), ...query })));
4498
+ route(GITHUB_ROUTES.createPullRequestReview, ({ path, body }) => {
4499
+ const { value, delta } = captureDelta((onDelta) => domain.createPullRequestReview({ ...pullRef(path), ...body }, onDelta));
3937
4500
  return created(value, delta);
3938
- }));
3939
- session.get("/repos/:owner/:repo/pulls/:number/comments", handle((c) => ok(domain.getPullRequestComments({ ...params(c), pull_number: numberParam(c, "number"), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
3940
- session.get("/repos/:owner/:repo/pulls/:number/status", handle((c) => ok(domain.getPullRequestStatus({ ...params(c), pull_number: numberParam(c, "number") }))));
3941
- session.put("/repos/:owner/:repo/pulls/:number/merge", handle(async (c) => {
3942
- const args = { ...params(c), pull_number: numberParam(c, "number"), ...mergeSchema.parse(await maybeJson(c)) };
4501
+ });
4502
+ route(GITHUB_ROUTES.listPullRequestComments, ({ path, query }) => ok(domain.getPullRequestComments({ ...pullRef(path), ...query })));
4503
+ route(GITHUB_ROUTES.getPullRequestStatus, ({ path }) => ok(domain.getPullRequestStatus(pullRef(path))));
4504
+ route(GITHUB_ROUTES.mergePullRequest, ({ path, body }, c) => {
4505
+ const args = { ...pullRef(path), ...body };
3943
4506
  const actor = sessionLogin(c);
3944
- if (!actor || !domain.hasRepositoryPermission({ owner: args.owner, repo: args.repo, username: actor, permissions: ["push", "maintain", "admin"] })) {
4507
+ if (!actor || !domain.hasRepositoryPermission({
4508
+ owner: args.owner,
4509
+ repo: args.repo,
4510
+ username: actor,
4511
+ permissions: ["push", "maintain", "admin"]
4512
+ })) {
3945
4513
  throw new TwinError("Must have push access to the repository to merge pull requests.", 403);
3946
4514
  }
3947
4515
  const { value, delta } = captureDelta((onDelta) => domain.mergePullRequest(args, onDelta));
3948
4516
  return ok(value, true, delta);
3949
- }));
3950
- session.put("/repos/:owner/:repo/pulls/:number/update-branch", handle(async (c) => {
3951
- const args = { ...params(c), pull_number: numberParam(c, "number"), ...updateBranchSchema.parse(await maybeJson(c)) };
3952
- const { value, delta } = captureDelta((onDelta) => domain.updatePullRequestBranch(args, onDelta));
4517
+ });
4518
+ route(GITHUB_ROUTES.updatePullRequestBranch, ({ path, body }) => {
4519
+ const { value, delta } = captureDelta((onDelta) => domain.updatePullRequestBranch({ ...pullRef(path), ...body }, onDelta));
3953
4520
  return ok(value, true, delta);
3954
- }));
3955
- session.get("/repos/:owner/:repo/branches", handle((c) => ok(domain.listBranchesForRepo({ ...params(c), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
3956
- session.get("/repos/:owner/:repo/branches/*", handle((c) => ok(domain.getBranchByName({ ...params(c), branch: routeTail(c, "branches/") }))));
3957
- session.delete("/repos/:owner/:repo/git/refs/heads/*", handle((c) => {
3958
- const args = { ...params(c), branch: routeTail(c, "git/refs/heads/") };
3959
- const { delta } = captureDelta((onDelta) => domain.deleteBranch(args, onDelta));
4521
+ });
4522
+ route(GITHUB_ROUTES.listBranches, ({ path, query }) => ok(domain.listBranchesForRepo({ ...path, ...query })));
4523
+ route(GITHUB_ROUTES.getBranch, ({ path }) => ok(domain.getBranchByName(path)));
4524
+ route(GITHUB_ROUTES.deleteBranch, ({ path }) => {
4525
+ const { delta } = captureDelta((onDelta) => domain.deleteBranch(path, onDelta));
3960
4526
  return { status: 204, body: null, mutation: true, delta };
3961
- }));
3962
- session.delete("/repos/:owner/:repo/contents/*", handle(async (c) => {
3963
- const body = deleteFileSchema.parse(await readJson(c));
3964
- const args = { ...params(c), path: contentPath(c), ...body };
3965
- const actor = sessionLogin(c);
3966
- const { value, delta } = captureDelta((onDelta) => domain.deleteFile(args, { actor }, onDelta));
4527
+ });
4528
+ route(GITHUB_ROUTES.deleteFile, ({ path, body }, c) => {
4529
+ const { value, delta } = captureDelta((onDelta) => domain.deleteFile({ ...path, ...body }, { actor: sessionLogin(c) }, onDelta));
3967
4530
  return ok(value, true, delta);
3968
- }));
3969
- session.get("/repos/:owner/:repo/commits/:ref", handle((c) => ok(domain.getCommitWithFiles({ ...params(c), ref: requireParam(c, "ref") }))));
3970
- session.get("/repos/:owner/:repo/compare/:basehead{.+}", handle((c) => {
3971
- const basehead = requireParam(c, "basehead");
3972
- const parts = basehead.split("...");
4531
+ });
4532
+ route(GITHUB_ROUTES.getCommit, ({ path }) => ok(domain.getCommitWithFiles(path)));
4533
+ route(GITHUB_ROUTES.compareCommits, ({ path }) => {
4534
+ const parts = path.basehead.split("...");
3973
4535
  if (parts.length !== 2 || !parts[0] || !parts[1]) {
3974
4536
  throw new TwinError("Invalid compare ref. Expected 'base...head'.", 422);
3975
4537
  }
3976
- return ok(domain.compareCommits({ ...params(c), base: parts[0], head: parts[1] }));
3977
- }));
3978
- session.get("/repos/:owner/:repo/pulls/:number/diff", handle((c) => ok(domain.getPullRequestDiff({ ...params(c), pull_number: numberParam(c, "number") }))));
3979
- session.patch("/repos/:owner/:repo/pulls/:number", handle(async (c) => {
3980
- const args = { ...params(c), pull_number: numberParam(c, "number"), ...updatePrSchema.parse(await readJson(c)) };
3981
- const { value, delta } = captureDelta((onDelta) => domain.updatePullRequest(args, onDelta));
4538
+ return ok(domain.compareCommits({ owner: path.owner, repo: path.repo, base: parts[0], head: parts[1] }));
4539
+ });
4540
+ route(GITHUB_ROUTES.getPullRequestDiff, ({ path }) => ok(domain.getPullRequestDiff(pullRef(path))));
4541
+ route(GITHUB_ROUTES.updatePullRequest, ({ path, body }) => {
4542
+ const { value, delta } = captureDelta((onDelta) => domain.updatePullRequest({ ...pullRef(path), ...body }, onDelta));
3982
4543
  return ok(value, true, delta);
3983
- }));
3984
- session.get("/repos/:owner/:repo/pulls/:number/commits", handle((c) => ok(domain.getPullRequestCommits({ ...params(c), pull_number: numberParam(c, "number"), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
3985
- session.post("/repos/:owner/:repo/pulls/:number/comments", handle(async (c) => {
3986
- const args = { ...params(c), pull_number: numberParam(c, "number"), ...reviewCommentSchema.parse(await readJson(c)) };
3987
- const actor = sessionLogin(c);
3988
- const { value, delta } = captureDelta((onDelta) => domain.createPullRequestReviewComment(args, { actor }, onDelta));
4544
+ });
4545
+ route(GITHUB_ROUTES.listPullRequestCommits, ({ path, query }) => ok(domain.getPullRequestCommits({ ...pullRef(path), ...query })));
4546
+ route(GITHUB_ROUTES.createPullRequestReviewComment, ({ path, body }, c) => {
4547
+ const { value, delta } = captureDelta((onDelta) => domain.createPullRequestReviewComment({ ...pullRef(path), ...body }, { actor: sessionLogin(c) }, onDelta));
3989
4548
  return created(value, delta);
3990
- }));
3991
- session.post("/repos/:owner/:repo/pulls/:number/comments/:comment_id/replies", handle(async (c) => {
3992
- const args = { ...params(c), pull_number: numberParam(c, "number"), comment_id: numberParam(c, "comment_id"), ...replyCommentSchema.parse(await readJson(c)) };
3993
- const actor = sessionLogin(c);
3994
- const { value, delta } = captureDelta((onDelta) => domain.addReplyToPullRequestComment(args, { actor }, onDelta));
4549
+ });
4550
+ route(GITHUB_ROUTES.replyToPullRequestReviewComment, ({ path, body }, c) => {
4551
+ const { value, delta } = captureDelta((onDelta) => domain.addReplyToPullRequestComment({ ...pullRef(path), comment_id: path.comment_id, ...body }, { actor: sessionLogin(c) }, onDelta));
3995
4552
  return created(value, delta);
3996
- }));
3997
- session.patch("/repos/:owner/:repo/issues/comments/:comment_id", handle(async (c) => {
3998
- const args = { ...params(c), comment_id: numberParam(c, "comment_id"), ...updateCommentSchema.parse(await readJson(c)) };
3999
- const { value, delta } = captureDelta((onDelta) => domain.updateIssueComment(args, onDelta));
4553
+ });
4554
+ route(GITHUB_ROUTES.updateIssueComment, ({ path, body }) => {
4555
+ const { value, delta } = captureDelta((onDelta) => domain.updateIssueComment({ ...path, ...body }, onDelta));
4000
4556
  return ok(value, true, delta);
4001
- }));
4002
- session.delete("/repos/:owner/:repo/issues/comments/:comment_id", handle((c) => {
4003
- const args = { ...params(c), comment_id: numberParam(c, "comment_id") };
4004
- const { delta } = captureDelta((onDelta) => domain.deleteIssueComment(args, onDelta));
4557
+ });
4558
+ route(GITHUB_ROUTES.deleteIssueComment, ({ path }) => {
4559
+ const { delta } = captureDelta((onDelta) => domain.deleteIssueComment(path, onDelta));
4005
4560
  return { status: 204, body: null, mutation: true, delta };
4006
- }));
4007
- session.get("/repos/:owner/:repo/milestones", handle((c) => ok(domain.listMilestones({ ...params(c), state: stateQuery(c), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
4008
- session.post("/repos/:owner/:repo/milestones", handle(async (c) => {
4009
- const args = { ...params(c), ...milestoneSchema.parse(await readJson(c)) };
4010
- const { value, delta } = captureDelta((onDelta) => domain.createMilestone(args, onDelta));
4561
+ });
4562
+ route(GITHUB_ROUTES.listMilestones, ({ path, query }) => ok(domain.listMilestones({ ...path, ...query })));
4563
+ route(GITHUB_ROUTES.createMilestone, ({ path, body }) => {
4564
+ const { value, delta } = captureDelta((onDelta) => domain.createMilestone({ ...path, ...body }, onDelta));
4011
4565
  return created(value, delta);
4012
- }));
4013
- session.patch("/repos/:owner/:repo/milestones/:number", handle(async (c) => {
4014
- const args = { ...params(c), milestone_number: numberParam(c, "number"), ...updateMilestoneSchema.parse(await readJson(c)) };
4015
- const { value, delta } = captureDelta((onDelta) => domain.updateMilestone(args, onDelta));
4566
+ });
4567
+ route(GITHUB_ROUTES.updateMilestone, ({ path, body }) => {
4568
+ const { value, delta } = captureDelta((onDelta) => domain.updateMilestone({ ...milestoneRef(path), ...body }, onDelta));
4016
4569
  return ok(value, true, delta);
4017
- }));
4018
- session.delete("/repos/:owner/:repo/milestones/:number", handle((c) => {
4019
- const args = { ...params(c), milestone_number: numberParam(c, "number") };
4020
- const { delta } = captureDelta((onDelta) => domain.deleteMilestone(args, onDelta));
4570
+ });
4571
+ route(GITHUB_ROUTES.deleteMilestone, ({ path }) => {
4572
+ const { delta } = captureDelta((onDelta) => domain.deleteMilestone(milestoneRef(path), onDelta));
4021
4573
  return { status: 204, body: null, mutation: true, delta };
4022
- }));
4023
- session.post("/repos/:owner/:repo/statuses/:sha", handleAs("create_commit_status", async (c) => {
4024
- const args = { ...params(c), sha: requireParam(c, "sha"), ...createStatusSchema.parse(await readJson(c)) };
4025
- const { value, delta } = captureDelta((onDelta) => domain.createCommitStatus(args, onDelta));
4574
+ });
4575
+ route(GITHUB_ROUTES.createCommitStatus, ({ path, body }) => {
4576
+ const { value, delta } = captureDelta((onDelta) => domain.createCommitStatus({ ...path, ...body }, onDelta));
4026
4577
  return created(value, delta);
4027
- }));
4028
- session.get("/repos/:owner/:repo/commits/:ref/status", handle((c) => ok(domain.getCombinedStatusForRef({ ...params(c), ref: requireParam(c, "ref") }))));
4029
- session.post("/repos/:owner/:repo/check-runs", handleAs("create_check_run", async (c) => {
4030
- const args = { ...params(c), ...createCheckRunSchema.parse(await readJson(c)) };
4031
- const { value, delta } = captureDelta((onDelta) => domain.createCheckRun(args, onDelta));
4578
+ }, "create_commit_status");
4579
+ route(GITHUB_ROUTES.getCombinedStatusForRef, ({ path }) => ok(domain.getCombinedStatusForRef(path)));
4580
+ route(GITHUB_ROUTES.createCheckRun, ({ path, body }) => {
4581
+ const { value, delta } = captureDelta((onDelta) => domain.createCheckRun({ ...path, ...body }, onDelta));
4032
4582
  return created(value, delta);
4033
- }));
4034
- session.get("/repos/:owner/:repo/commits/:ref/check-runs", handle((c) => ok(domain.listCheckRunsForRef({ ...params(c), ref: requireParam(c, "ref"), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
4035
- session.get("/repos/:owner/:repo/tags", handle((c) => ok(domain.listTags({ ...params(c), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
4036
- session.get("/repos/:owner/:repo/releases", handle((c) => ok(domain.listReleases({ ...params(c), page: numberQuery(c, "page"), per_page: numberQuery(c, "per_page") }))));
4037
- session.get("/repos/:owner/:repo/releases/latest", handle((c) => ok(domain.getLatestRelease(params(c)))));
4038
- session.get("/repos/:owner/:repo/releases/tags/*", handle((c) => ok(domain.getReleaseByTag({ ...params(c), tag: routeTail(c, "releases/tags/") }))));
4039
- session.post("/repos/:owner/:repo/releases", handle(async (c) => {
4040
- const args = { ...params(c), ...createReleaseSchema.parse(await readJson(c)) };
4041
- const actor = sessionLogin(c);
4042
- const { value, delta } = captureDelta((onDelta) => domain.createRelease(args, { actor }, onDelta));
4583
+ }, "create_check_run");
4584
+ route(GITHUB_ROUTES.listCheckRunsForRef, ({ path, query }) => ok(domain.listCheckRunsForRef({ ...path, ...query })));
4585
+ route(GITHUB_ROUTES.listTags, ({ path, query }) => ok(domain.listTags({ ...path, ...query })));
4586
+ route(GITHUB_ROUTES.listReleases, ({ path, query }) => ok(domain.listReleases({ ...path, ...query })));
4587
+ route(GITHUB_ROUTES.getLatestRelease, ({ path }) => ok(domain.getLatestRelease(path)));
4588
+ route(GITHUB_ROUTES.getReleaseByTag, ({ path }) => ok(domain.getReleaseByTag(path)));
4589
+ route(GITHUB_ROUTES.createRelease, ({ path, body }, c) => {
4590
+ const { value, delta } = captureDelta((onDelta) => domain.createRelease({ ...path, ...body }, { actor: sessionLogin(c) }, onDelta));
4043
4591
  return created(value, delta);
4044
- }));
4045
- session.get("/user", handle((c) => ok(domain.getMe({ actor: sessionLogin(c) }))));
4046
- session.put("/repos/:owner/:repo/collaborators/:username", handle(async (c) => {
4047
- const body = addCollaboratorSchema.parse(await maybeJson(c));
4592
+ });
4593
+ route(GITHUB_ROUTES.getAuthenticatedUser, (_input, c) => ok(domain.getMe({ actor: sessionLogin(c) })));
4594
+ route(GITHUB_ROUTES.addCollaborator, ({ path, body }, c) => {
4048
4595
  const actor = sessionLogin(c);
4049
- if (!actor || !domain.hasRepositoryPermission({ ...params(c), username: actor, permissions: ["push", "maintain", "admin"] })) {
4596
+ if (!actor || !domain.hasRepositoryPermission({
4597
+ owner: path.owner,
4598
+ repo: path.repo,
4599
+ username: actor,
4600
+ permissions: ["push", "maintain", "admin"]
4601
+ })) {
4050
4602
  throw new TwinError("Must have push access to the repository to add collaborators.", 403);
4051
4603
  }
4052
- const args = { ...params(c), username: requireParam(c, "username"), permission: body.permission };
4053
- const { value, delta } = captureDelta((onDelta) => domain.addCollaboratorAction({ ...args, actor }, onDelta));
4604
+ const { value, delta } = captureDelta((onDelta) => domain.addCollaboratorAction({ ...path, permission: body.permission, actor }, onDelta));
4054
4605
  return { status: value.status, body: value.body, mutation: true, delta };
4055
- }));
4606
+ });
4607
+ }
4608
+ async function parseDeclared(declaration, c) {
4609
+ try {
4610
+ return await declaration.parse(c.req);
4611
+ } catch (error) {
4612
+ if (error instanceof UndeclaredInputError)
4613
+ validationFailed(error.first, "invalid");
4614
+ if (error instanceof MalformedBodyError)
4615
+ throw new SyntaxError("Problems parsing JSON");
4616
+ throw error;
4617
+ }
4056
4618
  }
4057
4619
  function ok(body, mutation = false, delta = null) {
4058
4620
  return { status: 200, body, mutation, delta };
@@ -4060,59 +4622,18 @@ function ok(body, mutation = false, delta = null) {
4060
4622
  function created(body, delta = null) {
4061
4623
  return { status: 201, body, mutation: true, delta };
4062
4624
  }
4063
- async function readJson(c) {
4064
- try {
4065
- return await c.req.json();
4066
- } catch {
4067
- throw new SyntaxError("Problems parsing JSON");
4068
- }
4069
- }
4070
- async function maybeJson(c) {
4071
- try {
4072
- return await c.req.json();
4073
- } catch {
4074
- return {};
4075
- }
4076
- }
4077
- function params(c) {
4078
- return { owner: requireParam(c, "owner"), repo: requireParam(c, "repo") };
4079
- }
4080
4625
  function sessionLogin(c) {
4081
4626
  const session = c.get("session");
4082
4627
  return typeof session?.login === "string" ? session.login : void 0;
4083
4628
  }
4084
- function numberParam(c, name) {
4085
- const value = Number(c.req.param(name));
4086
- if (!Number.isInteger(value) || value < 1)
4087
- validationFailed(name, "invalid", c.req.param(name));
4088
- return value;
4089
- }
4090
- function requireParam(c, name) {
4091
- const value = c.req.param(name);
4092
- if (!value)
4093
- throw new TwinError(`Missing route parameter: ${name}`, 400);
4094
- return value;
4095
- }
4096
- function contentPath(c) {
4097
- return routeTail(c, "contents/");
4098
- }
4099
- function routeTail(c, marker) {
4100
- const { owner, repo } = params(c);
4101
- const pathname = new URL(c.req.url).pathname;
4102
- const sid = pathname.match(/^\/s\/([^/]+)\//)?.[1] ?? "";
4103
- const prefix = `/s/${sid}/repos/${owner}/${repo}/${marker}`;
4104
- const value = decodeURIComponent(pathname.startsWith(prefix) ? pathname.slice(prefix.length) : "");
4105
- if (!value)
4106
- throw new TwinError(`Missing route path after ${marker}`, 400);
4107
- return value;
4108
- }
4109
- function numberQuery(c, name) {
4110
- const value = c.req.query(name);
4111
- return value ? Number(value) : void 0;
4112
- }
4113
- function stateQuery(c) {
4114
- const state = c.req.query("state");
4115
- return state === "open" || state === "closed" || state === "all" ? state : void 0;
4629
+ function issueRef(path) {
4630
+ return { owner: path.owner, repo: path.repo, issue_number: path.number };
4631
+ }
4632
+ function pullRef(path) {
4633
+ return { owner: path.owner, repo: path.repo, pull_number: path.number };
4634
+ }
4635
+ function milestoneRef(path) {
4636
+ return { owner: path.owner, repo: path.repo, milestone_number: path.number };
4116
4637
  }
4117
4638
 
4118
4639
  // ../packages/twin-github/dist/src/unsupported-envelope.js