1import * as Schema from "effect/Schema";
2import { PositiveInt, TrimmedNonEmptyString } from "./baseSchemas.ts";
3import { VcsDriverKind } from "./vcs.ts";
4
5/** Accept unknown hosts so clients can fall back to generic presentation. */
6export const SourceControlProviderKind = TrimmedNonEmptyString.check(
7 Schema.isMaxLength(64),
8 Schema.isPattern(/^[a-z][a-z0-9-]*$/),
9).pipe(Schema.brand("SourceControlProviderKind"));
10export type SourceControlProviderKind = typeof SourceControlProviderKind.Type;
11
12export const SourceControlProviderInfo = Schema.Struct({
13 kind: SourceControlProviderKind,
14 name: TrimmedNonEmptyString,
15 baseUrl: Schema.String,
16});
17export type SourceControlProviderInfo = typeof SourceControlProviderInfo.Type;
18
19export const ChangeRequestState = Schema.Literals(["open", "closed", "merged"]);
20export type ChangeRequestState = typeof ChangeRequestState.Type;
21
22export const ChangeRequest = Schema.Struct({
23 provider: SourceControlProviderKind,
24 number: PositiveInt,
25 title: TrimmedNonEmptyString,
26 url: Schema.String,
27 baseRefName: TrimmedNonEmptyString,
28 headRefName: TrimmedNonEmptyString,
29 headSha: Schema.optional(TrimmedNonEmptyString),
30 state: ChangeRequestState,
31 isDraft: Schema.optional(Schema.Boolean),
32 closedAt: Schema.optional(Schema.NullOr(Schema.String)),
33 mergedAt: Schema.optional(Schema.NullOr(Schema.String)),
34 updatedAt: Schema.Option(Schema.DateTimeUtc),
35 isCrossRepository: Schema.optional(Schema.Boolean),
36 headRepositoryNameWithOwner: Schema.optional(Schema.NullOr(TrimmedNonEmptyString)),
37 headRepositoryOwnerLogin: Schema.optional(Schema.NullOr(TrimmedNonEmptyString)),
38});
39export type ChangeRequest = typeof ChangeRequest.Type;
40
41export const SourceControlRepositoryCloneUrls = Schema.Struct({
42 nameWithOwner: TrimmedNonEmptyString,
43 url: TrimmedNonEmptyString,
44 sshUrl: TrimmedNonEmptyString,
45});
46export type SourceControlRepositoryCloneUrls = typeof SourceControlRepositoryCloneUrls.Type;
47
48export const SourceControlRepositoryVisibility = Schema.Literals(["private", "public"]);
49export type SourceControlRepositoryVisibility = typeof SourceControlRepositoryVisibility.Type;
50
51export const SourceControlCloneProtocol = Schema.Literals(["auto", "ssh", "https"]);
52export type SourceControlCloneProtocol = typeof SourceControlCloneProtocol.Type;
53
54export const SourceControlRepositoryInfo = Schema.Struct({
55 provider: SourceControlProviderKind,
56 nameWithOwner: TrimmedNonEmptyString,
57 url: TrimmedNonEmptyString,
58 sshUrl: TrimmedNonEmptyString,
59});
60export type SourceControlRepositoryInfo = typeof SourceControlRepositoryInfo.Type;
61
62export const SourceControlRepositoryLookupInput = Schema.Struct({
63 provider: SourceControlProviderKind,
64 repository: TrimmedNonEmptyString,
65 cwd: Schema.optional(TrimmedNonEmptyString),
66});
67export type SourceControlRepositoryLookupInput = typeof SourceControlRepositoryLookupInput.Type;
68
69export const SourceControlCloneRepositoryInput = Schema.Struct({
70 provider: Schema.optional(SourceControlProviderKind),
71 repository: Schema.optional(TrimmedNonEmptyString),
72 remoteUrl: Schema.optional(TrimmedNonEmptyString),
73 destinationPath: TrimmedNonEmptyString,
74 protocol: Schema.optional(SourceControlCloneProtocol),
75});
76export type SourceControlCloneRepositoryInput = typeof SourceControlCloneRepositoryInput.Type;
77
78export const SourceControlCloneRepositoryResult = Schema.Struct({
79 cwd: TrimmedNonEmptyString,
80 remoteUrl: TrimmedNonEmptyString,
81 repository: Schema.NullOr(SourceControlRepositoryInfo),
82});
83export type SourceControlCloneRepositoryResult = typeof SourceControlCloneRepositoryResult.Type;
84
85export const SourceControlPublishRepositoryInput = Schema.Struct({
86 cwd: TrimmedNonEmptyString,
87 provider: SourceControlProviderKind,
88 repository: TrimmedNonEmptyString,
89 visibility: SourceControlRepositoryVisibility,
90 remoteName: Schema.optional(TrimmedNonEmptyString),
91 protocol: Schema.optional(SourceControlCloneProtocol),
92});
93export type SourceControlPublishRepositoryInput = typeof SourceControlPublishRepositoryInput.Type;
94
95export const SourceControlPublishStatus = Schema.Literals(["pushed", "remote_added"]);
96export type SourceControlPublishStatus = typeof SourceControlPublishStatus.Type;
97
98export const SourceControlPublishRepositoryResult = Schema.Struct({
99 repository: SourceControlRepositoryInfo,
100 remoteName: TrimmedNonEmptyString,
101 remoteUrl: TrimmedNonEmptyString,
102 branch: TrimmedNonEmptyString,
103 status: SourceControlPublishStatus,
104});
105export type SourceControlPublishRepositoryResult = typeof SourceControlPublishRepositoryResult.Type;
106
107export const SourceControlDiscoveryStatus = Schema.Literals(["available", "missing"]);
108export type SourceControlDiscoveryStatus = typeof SourceControlDiscoveryStatus.Type;
109
110export const SourceControlProviderAuthStatus = Schema.Literals([
111 "authenticated",
112 "unauthenticated",
113 "unknown",
114]);
115export type SourceControlProviderAuthStatus = typeof SourceControlProviderAuthStatus.Type;
116
117export const SourceControlProviderAuth = Schema.Struct({
118 status: SourceControlProviderAuthStatus,
119 account: Schema.Option(TrimmedNonEmptyString),
120 host: Schema.Option(TrimmedNonEmptyString),
121 detail: Schema.Option(TrimmedNonEmptyString),
122 accounts: Schema.optionalKey(
123 Schema.Array(
124 Schema.Struct({
125 host: TrimmedNonEmptyString,
126 account: TrimmedNonEmptyString,
127 active: Schema.Boolean,
128 authenticated: Schema.Boolean,
129 error: Schema.optionalKey(TrimmedNonEmptyString),
130 /** Set when the login comes from a token variable such as `GH_TOKEN`, which wins over Settings. */
131 environmentVariable: Schema.optionalKey(TrimmedNonEmptyString),
132 }),
133 ),
134 ),
135});
136export type SourceControlProviderAuth = typeof SourceControlProviderAuth.Type;
137
138const SourceControlDiscoverySharedFields = {
139 label: TrimmedNonEmptyString,
140 executable: Schema.optional(TrimmedNonEmptyString),
141 status: SourceControlDiscoveryStatus,
142 version: Schema.Option(TrimmedNonEmptyString),
143 installHint: TrimmedNonEmptyString,
144 detail: Schema.Option(TrimmedNonEmptyString),
145} as const;
146
147export const VcsDiscoveryItem = Schema.Struct({
148 kind: VcsDriverKind,
149 implemented: Schema.Boolean,
150 ...SourceControlDiscoverySharedFields,
151});
152export type VcsDiscoveryItem = typeof VcsDiscoveryItem.Type;
153
154export const SourceControlProviderDiscoveryItem = Schema.Struct({
155 kind: SourceControlProviderKind,
156 ...SourceControlDiscoverySharedFields,
157 auth: SourceControlProviderAuth,
158 /** The server is configured to sign in through the host's app; clients offer it only then. */
159 appSignIn: Schema.optionalKey(Schema.Boolean),
160});
161export type SourceControlProviderDiscoveryItem = typeof SourceControlProviderDiscoveryItem.Type;
162
163export const SourceControlDiscoveryResult = Schema.Struct({
164 versionControlSystems: Schema.Array(VcsDiscoveryItem),
165 sourceControlProviders: Schema.Array(SourceControlProviderDiscoveryItem),
166});
167export type SourceControlDiscoveryResult = typeof SourceControlDiscoveryResult.Type;
168
169export class SourceControlProviderError extends Schema.TaggedError<SourceControlProviderError>()(
170 "SourceControlProviderError",
171 {
172 provider: SourceControlProviderKind,
173 operation: Schema.String,
174 cwd: Schema.String,
175 command: Schema.optional(Schema.String),
176 repository: Schema.optional(Schema.String),
177 reference: Schema.optional(Schema.String),
178 detail: Schema.String,
179 cause: Schema.optional(Schema.Defect()),
180 },
181) {
182 override get message(): string {
183 return `Source control provider ${this.provider} failed in ${this.operation}: ${this.detail}`;
184 }
185}
186
187export const SourceControlSignInInput = Schema.Struct({
188 provider: SourceControlProviderKind,
189 host: TrimmedNonEmptyString,
190});
191export type SourceControlSignInInput = typeof SourceControlSignInInput.Type;
192
193/** A device-flow code the user enters at `verificationUri` in any browser. */
194export const SourceControlSignInPrompt = Schema.Struct({
195 userCode: TrimmedNonEmptyString,
196 verificationUri: TrimmedNonEmptyString,
197 expiresAt: Schema.Number,
198});
199export type SourceControlSignInPrompt = typeof SourceControlSignInPrompt.Type;
200
201export const SourceControlSignInResult = Schema.Struct({
202 account: TrimmedNonEmptyString,
203});
204export type SourceControlSignInResult = typeof SourceControlSignInResult.Type;
205
206export const SourceControlSignInFailure = Schema.Literals([
207 "unsupported",
208 "not-started",
209 "cancelled",
210 "denied",
211 "expired",
212 "failed",
213]);
214export type SourceControlSignInFailure = typeof SourceControlSignInFailure.Type;
215
216export const SourceControlAppAccessInput = Schema.Struct({
217 provider: SourceControlProviderKind,
218 host: TrimmedNonEmptyString,
219 repository: TrimmedNonEmptyString,
220});
221export type SourceControlAppAccessInput = typeof SourceControlAppAccessInput.Type;
222
223/** Whether the server's forge app sign-in reaches a repository; `signed-out` when not in use. */
224export const SourceControlAppAccess = Schema.Struct({
225 status: Schema.Literals(["covered", "not-covered", "signed-out"]),
226});
227export type SourceControlAppAccess = typeof SourceControlAppAccess.Type;
228
229export const SourceControlStarredInput = Schema.Struct({
230 provider: SourceControlProviderKind,
231 host: TrimmedNonEmptyString,
232 repository: TrimmedNonEmptyString,
233});
234export type SourceControlStarredInput = typeof SourceControlStarredInput.Type;
235
236export class SourceControlSignInError extends Schema.TaggedError<SourceControlSignInError>()(
237 "SourceControlSignInError",
238 {
239 provider: SourceControlProviderKind,
240 host: Schema.String,
241 reason: SourceControlSignInFailure,
242 detail: Schema.String,
243 cause: Schema.optional(Schema.Defect()),
244 },
245) {
246 override get message(): string {
247 return this.detail;
248 }
249}
250
251export class SourceControlRepositoryError extends Schema.TaggedError<SourceControlRepositoryError>()(
252 "SourceControlRepositoryError",
253 {
254 provider: SourceControlProviderKind,
255 operation: Schema.String,
256 detail: Schema.String,
257 cause: Schema.optional(Schema.Defect()),
258 },
259) {
260 override get message(): string {
261 return `Source control repository operation ${this.operation} failed for ${this.provider}: ${this.detail}`;
262 }
263}