@openchamber/sdk 2.5.0 → 2.5.2
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/API.md +33 -4
- package/DOCUMENTATION.md +41 -7
- package/GUEST_SERVICES.md +129 -3
- package/dist/index.d.ts +7 -5
- package/dist/index.js +3 -2
- package/dist/manifest.d.ts +188 -5
- package/dist/manifest.js +39 -5
- package/dist/parse.d.ts +230 -2
- package/dist/parse.js +198 -7
- package/dist/protocol.d.ts +30 -0
- package/dist/schemas.d.ts +2 -1
- package/dist/schemas.js +2 -1
- package/dist/service-provider-schemas.d.ts +510 -0
- package/dist/service-provider-schemas.js +221 -1
- package/dist/service-providers.d.ts +377 -2
- package/dist/service-providers.js +216 -0
- package/dist/status-view-schemas.d.ts +166 -0
- package/dist/status-view-schemas.js +113 -0
- package/dist/status-view.d.ts +129 -0
- package/dist/status-view.js +15 -0
- package/dist/ui/style.js +16 -9
- package/dist/workspace-schemas.d.ts +9 -0
- package/dist/workspace-schemas.js +1 -0
- package/dist/workspace.d.ts +13 -0
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { BROWSER_CONTROL_ACTIONS } from './service-providers.js';
|
|
2
|
+
import { BROWSER_CONTROL_ACTIONS, SOURCE_CONTROL_OUTBOUND_BODY_MAX, SOURCE_CONTROL_OUTBOUND_METHODS, SOURCE_CONTROL_OUTBOUND_REQUEST_HEADERS, SOURCE_CONTROL_PROVIDER_ERROR_MESSAGE_MAX, SOURCE_CONTROL_PROVIDER_METHODS, SOURCE_CONTROL_PROVIDER_RESPONSE_MAX, SOURCE_CONTROL_PROVIDER_TARGET_MAX, } from './service-providers.js';
|
|
3
3
|
/**
|
|
4
4
|
* Host-side parse of what a browser provider answers. `data` stays whatever
|
|
5
5
|
* the service produced: the host hands it to the agent as is, and only
|
|
@@ -10,3 +10,223 @@ export const browserProviderResultSchema = z.union([
|
|
|
10
10
|
z.object({ ok: z.literal(false), error: z.string().min(1).max(2_000) }),
|
|
11
11
|
]);
|
|
12
12
|
export const browserControlActionSchema = z.enum(BROWSER_CONTROL_ACTIONS);
|
|
13
|
+
/*
|
|
14
|
+
* Host-side parse of what a source-control provider answers. Every answer is
|
|
15
|
+
* checked as an envelope. Answers OpenChamber acts on (who an account is,
|
|
16
|
+
* where a head lives, what a mutation targets and did) are checked deeply;
|
|
17
|
+
* reads OpenChamber only hands to the UI, which parses those DTOs itself, are
|
|
18
|
+
* checked for being a JSON object or array within the size cap, so the two
|
|
19
|
+
* parsers never drift apart. A failed parse is a malformed answer, never an
|
|
20
|
+
* empty one.
|
|
21
|
+
*/
|
|
22
|
+
const text = z.string().min(1).max(1_024);
|
|
23
|
+
const optionalText = z.string().max(1_024).optional();
|
|
24
|
+
const positiveInteger = z.number().int().positive().max(Number.MAX_SAFE_INTEGER);
|
|
25
|
+
const nonNegativeInteger = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
26
|
+
const commitSha = z.string().regex(/^[0-9a-fA-F]{40}$/).transform((sha) => sha.toLowerCase());
|
|
27
|
+
const projectRef = z.object({ id: text, owner: text, name: text }).strict();
|
|
28
|
+
const errorSchema = z.object({
|
|
29
|
+
message: z.string().min(1).max(SOURCE_CONTROL_PROVIDER_ERROR_MESSAGE_MAX),
|
|
30
|
+
status: z.number().int().min(400).max(599).optional(),
|
|
31
|
+
code: z.string().regex(/^[A-Z_]{1,64}$/).optional(),
|
|
32
|
+
}).strict();
|
|
33
|
+
const envelopeSchema = z.union([
|
|
34
|
+
z.object({ ok: z.literal(true), data: z.json() }).strict(),
|
|
35
|
+
z.object({ ok: z.literal(false), error: errorSchema }).strict(),
|
|
36
|
+
]);
|
|
37
|
+
const forgeUserSchema = z.object({
|
|
38
|
+
id: positiveInteger,
|
|
39
|
+
login: z.string().min(1).max(256),
|
|
40
|
+
name: optionalText,
|
|
41
|
+
email: optionalText,
|
|
42
|
+
avatarUrl: z.string().max(2_048).optional(),
|
|
43
|
+
});
|
|
44
|
+
const verifySchema = z.object({ user: forgeUserSchema });
|
|
45
|
+
const probeSchema = z.object({
|
|
46
|
+
supported: z.boolean(),
|
|
47
|
+
version: z.string().max(128).optional(),
|
|
48
|
+
message: z.string().max(SOURCE_CONTROL_PROVIDER_ERROR_MESSAGE_MAX).optional(),
|
|
49
|
+
});
|
|
50
|
+
const changeRequestHeadSchema = z.object({
|
|
51
|
+
project: projectRef,
|
|
52
|
+
headSha: commitSha,
|
|
53
|
+
headBranch: text,
|
|
54
|
+
headOwner: text,
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* The shape only; OpenChamber also checks that `endpoint` is https on the
|
|
58
|
+
* instance origin and names `sourceProject`, and that `headSha` is the head it
|
|
59
|
+
* expected.
|
|
60
|
+
*/
|
|
61
|
+
const changeRequestSourceSchema = z.object({
|
|
62
|
+
sourceProject: projectRef,
|
|
63
|
+
targetProject: projectRef,
|
|
64
|
+
classification: z.enum(['same-repository', 'contributor-fork']),
|
|
65
|
+
headSha: commitSha,
|
|
66
|
+
headRef: z.string().regex(/^refs\/heads\/\S+$/).max(1_024),
|
|
67
|
+
endpoint: z.string().min(1).max(2_048),
|
|
68
|
+
});
|
|
69
|
+
/** Exactly the target keys OpenChamber's mutation store accepts. */
|
|
70
|
+
const mutationTargetSchema = z.object({
|
|
71
|
+
repositoryId: text,
|
|
72
|
+
bindingRevision: nonNegativeInteger,
|
|
73
|
+
primaryRemote: text,
|
|
74
|
+
project: projectRef,
|
|
75
|
+
number: positiveInteger.optional(),
|
|
76
|
+
head: text.optional(),
|
|
77
|
+
base: text.optional(),
|
|
78
|
+
headSha: text.optional(),
|
|
79
|
+
}).strict();
|
|
80
|
+
const jsonObject = z.record(z.string(), z.json());
|
|
81
|
+
const mutationResolutionSchema = z.object({
|
|
82
|
+
providerTarget: jsonObject.refine((target) => JSON.stringify(target).length <= SOURCE_CONTROL_PROVIDER_TARGET_MAX, { message: `providerTarget is at most ${SOURCE_CONTROL_PROVIDER_TARGET_MAX} characters of JSON` }),
|
|
83
|
+
target: mutationTargetSchema,
|
|
84
|
+
}).strict();
|
|
85
|
+
/** Exactly the result keys OpenChamber's mutation store accepts. */
|
|
86
|
+
const mutationResultSchema = z.object({
|
|
87
|
+
project: projectRef.optional(),
|
|
88
|
+
number: positiveInteger.optional(),
|
|
89
|
+
head: text.optional(),
|
|
90
|
+
base: text.optional(),
|
|
91
|
+
headSha: text.optional(),
|
|
92
|
+
state: text.optional(),
|
|
93
|
+
merged: z.boolean().optional(),
|
|
94
|
+
outcome: z.enum(['queued', 'merging']).optional(),
|
|
95
|
+
stacked: z.boolean().optional(),
|
|
96
|
+
ready: z.boolean().optional(),
|
|
97
|
+
commented: z.boolean().optional(),
|
|
98
|
+
failureStatus: z.number().int().min(400).max(599).optional(),
|
|
99
|
+
failureCode: text.optional(),
|
|
100
|
+
}).strict();
|
|
101
|
+
const reconcileSchema = z.object({
|
|
102
|
+
state: z.enum(['succeeded', 'failed', 'outcome-unknown']),
|
|
103
|
+
result: mutationResultSchema.optional(),
|
|
104
|
+
}).strict();
|
|
105
|
+
const LIVE_SUMMARIES_MAX = 50;
|
|
106
|
+
const liveChangeRequestSummarySchema = z.object({
|
|
107
|
+
owner: text,
|
|
108
|
+
repo: text,
|
|
109
|
+
number: positiveInteger,
|
|
110
|
+
state: z.enum(['open', 'closed', 'merged']),
|
|
111
|
+
draft: z.boolean(),
|
|
112
|
+
title: z.string().max(4_096),
|
|
113
|
+
mergeable: z.boolean().nullable(),
|
|
114
|
+
mergeableState: z.string().max(64).nullable(),
|
|
115
|
+
checks: z.object({ state: z.string().max(64) }).passthrough().nullable(),
|
|
116
|
+
headSha: z.string().max(128).optional(),
|
|
117
|
+
base: z.string().max(1_024).optional(),
|
|
118
|
+
});
|
|
119
|
+
const liveIssueSummarySchema = z.object({
|
|
120
|
+
owner: text,
|
|
121
|
+
repo: text,
|
|
122
|
+
number: positiveInteger,
|
|
123
|
+
title: z.string().max(4_096),
|
|
124
|
+
state: z.enum(['open', 'completed', 'not_planned']),
|
|
125
|
+
});
|
|
126
|
+
const liveSummariesSchema = z.object({
|
|
127
|
+
summaries: z.array(liveChangeRequestSummarySchema).max(LIVE_SUMMARIES_MAX),
|
|
128
|
+
issueSummaries: z.array(liveIssueSummarySchema).max(LIVE_SUMMARIES_MAX),
|
|
129
|
+
});
|
|
130
|
+
const identitySchema = z.object({ provider: text, instance: text }).passthrough();
|
|
131
|
+
const projectSchema = z.object({ provider: text, instance: text, id: text, owner: text, name: text, url: text }).passthrough();
|
|
132
|
+
const STACK_LAYERS_MAX = 50;
|
|
133
|
+
/** Change requests stacked on one another, bottom layer first (`PullRequestStack` in the UI). */
|
|
134
|
+
const changeRequestStackSchema = z.object({
|
|
135
|
+
number: positiveInteger.nullable(),
|
|
136
|
+
base: text,
|
|
137
|
+
layers: z.array(z.object({
|
|
138
|
+
number: positiveInteger,
|
|
139
|
+
title: z.string().max(4_096),
|
|
140
|
+
url: text,
|
|
141
|
+
state: z.enum(['open', 'closed', 'merged']),
|
|
142
|
+
draft: z.boolean(),
|
|
143
|
+
head: text,
|
|
144
|
+
}).passthrough()).max(STACK_LAYERS_MAX),
|
|
145
|
+
}).passthrough();
|
|
146
|
+
/**
|
|
147
|
+
* The local branch tip as a change-based host sees it: its sha, the change id
|
|
148
|
+
* its commit message carries (null when none), and the revision number it was
|
|
149
|
+
* uploaded as (null when this commit is not on the host yet).
|
|
150
|
+
*/
|
|
151
|
+
const localRevisionSchema = z.object({
|
|
152
|
+
sha: commitSha,
|
|
153
|
+
changeId: z.string().min(1).max(128).nullable(),
|
|
154
|
+
uploadedAs: positiveInteger.nullable(),
|
|
155
|
+
}).strict();
|
|
156
|
+
/** The status OpenChamber caches per branch and shows on every badge. */
|
|
157
|
+
const changeRequestStatusSchema = z.object({
|
|
158
|
+
identity: identitySchema,
|
|
159
|
+
fetchedAt: z.number().optional(),
|
|
160
|
+
project: projectSchema.nullable(),
|
|
161
|
+
branch: z.string().min(1).max(1_024),
|
|
162
|
+
changeRequest: z.object({
|
|
163
|
+
provider: text,
|
|
164
|
+
instance: text,
|
|
165
|
+
id: text,
|
|
166
|
+
number: positiveInteger,
|
|
167
|
+
project: projectSchema,
|
|
168
|
+
title: z.string().max(4_096),
|
|
169
|
+
url: text,
|
|
170
|
+
state: z.enum(['open', 'closed', 'merged']),
|
|
171
|
+
draft: z.boolean(),
|
|
172
|
+
base: text,
|
|
173
|
+
head: text,
|
|
174
|
+
}).passthrough().nullable(),
|
|
175
|
+
ci: z.object({ summary: z.object({ state: z.string().max(64) }).passthrough() }).passthrough().nullable().optional(),
|
|
176
|
+
canMerge: z.boolean().optional(),
|
|
177
|
+
defaultBranch: z.string().max(1_024).nullable().optional(),
|
|
178
|
+
resolvedRemoteName: z.string().max(1_024).nullable().optional(),
|
|
179
|
+
localRevision: localRevisionSchema.optional(),
|
|
180
|
+
stack: changeRequestStackSchema.nullable().optional(),
|
|
181
|
+
}).strict();
|
|
182
|
+
/** A read OpenChamber passes on unread: a JSON object or array within the response cap. */
|
|
183
|
+
const passThroughSchema = z.union([jsonObject, z.array(z.json())]).refine((value) => JSON.stringify(value).length <= SOURCE_CONTROL_PROVIDER_RESPONSE_MAX, { message: 'answer is larger than the response cap' });
|
|
184
|
+
const dataSchemas = {
|
|
185
|
+
'auth.verify': verifySchema,
|
|
186
|
+
'auth.probe': probeSchema,
|
|
187
|
+
changeRequestStatus: changeRequestStatusSchema,
|
|
188
|
+
listChangeRequests: passThroughSchema,
|
|
189
|
+
changeRequestContext: passThroughSchema,
|
|
190
|
+
listIssues: passThroughSchema,
|
|
191
|
+
getIssue: passThroughSchema,
|
|
192
|
+
issueComments: passThroughSchema,
|
|
193
|
+
projectUpstream: passThroughSchema,
|
|
194
|
+
projectBranches: passThroughSchema,
|
|
195
|
+
listLabels: passThroughSchema,
|
|
196
|
+
listReviewerCandidates: passThroughSchema,
|
|
197
|
+
readChangeRequestPatch: passThroughSchema,
|
|
198
|
+
readChangeRequestFile: passThroughSchema,
|
|
199
|
+
liveSummaries: liveSummariesSchema,
|
|
200
|
+
readChangeRequestHead: changeRequestHeadSchema,
|
|
201
|
+
resolveChangeRequestSource: changeRequestSourceSchema,
|
|
202
|
+
'mutation.resolveTarget': mutationResolutionSchema,
|
|
203
|
+
'mutation.perform': mutationResultSchema,
|
|
204
|
+
'mutation.reconcile': reconcileSchema,
|
|
205
|
+
};
|
|
206
|
+
/** What a service sends OpenChamber's outbound proxy; checked before anything reaches the forge. */
|
|
207
|
+
const outboundRequestSchema = z.object({
|
|
208
|
+
method: z.enum(SOURCE_CONTROL_OUTBOUND_METHODS),
|
|
209
|
+
path: z.string().min(1).max(2_048).regex(/^\/(?!\/)/),
|
|
210
|
+
query: z.record(z.string().max(256), z.string().max(2_048)).optional(),
|
|
211
|
+
headers: z.partialRecord(z.enum(SOURCE_CONTROL_OUTBOUND_REQUEST_HEADERS), z.string().max(1_024)).optional(),
|
|
212
|
+
body: z.string().max(SOURCE_CONTROL_OUTBOUND_BODY_MAX).optional(),
|
|
213
|
+
}).strict();
|
|
214
|
+
/** Wire 1 of the source-control role, as the host parses it. `data[method]` checks an `ok: true` answer's `data`. */
|
|
215
|
+
export const sourceControlProviderSchemasV1 = {
|
|
216
|
+
envelope: envelopeSchema,
|
|
217
|
+
error: errorSchema,
|
|
218
|
+
forgeUser: forgeUserSchema,
|
|
219
|
+
probe: probeSchema,
|
|
220
|
+
changeRequestHead: changeRequestHeadSchema,
|
|
221
|
+
changeRequestSource: changeRequestSourceSchema,
|
|
222
|
+
mutationTarget: mutationTargetSchema,
|
|
223
|
+
mutationResolution: mutationResolutionSchema,
|
|
224
|
+
mutationResult: mutationResultSchema,
|
|
225
|
+
reconcile: reconcileSchema,
|
|
226
|
+
liveSummaries: liveSummariesSchema,
|
|
227
|
+
changeRequestStatus: changeRequestStatusSchema,
|
|
228
|
+
passThrough: passThroughSchema,
|
|
229
|
+
outboundRequest: outboundRequestSchema,
|
|
230
|
+
data: dataSchemas,
|
|
231
|
+
};
|
|
232
|
+
export const sourceControlProviderMethodSchema = z.enum(SOURCE_CONTROL_PROVIDER_METHODS);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
/** Route on the service loopback the host posts every action to. */
|
|
12
12
|
export declare const BROWSER_PROVIDER_PATH = "/browser-control";
|
|
13
|
-
export declare const BROWSER_CONTROL_ACTIONS: readonly ['browser.open', 'browser.snapshot', 'browser.click', 'browser.type', 'browser.scroll', 'browser.back', 'browser.forward', 'browser.inspect', 'browser.capture', 'browser.resize', 'browser.close'];
|
|
13
|
+
export declare const BROWSER_CONTROL_ACTIONS: readonly ['browser.open', 'browser.snapshot', 'browser.click', 'browser.type', 'browser.select', 'browser.scroll', 'browser.back', 'browser.forward', 'browser.inspect', 'browser.capture', 'browser.resize', 'browser.close'];
|
|
14
14
|
export type BrowserControlAction = (typeof BROWSER_CONTROL_ACTIONS)[number];
|
|
15
15
|
export declare const BROWSER_VIEWPORT_MODES: readonly ['mobile', 'tablet', 'desktop', 'fill'];
|
|
16
16
|
export type BrowserViewportMode = (typeof BROWSER_VIEWPORT_MODES)[number];
|
|
@@ -48,6 +48,15 @@ export type BrowserTypeParameters = BrowserTabTarget & {
|
|
|
48
48
|
value: string;
|
|
49
49
|
submit: boolean;
|
|
50
50
|
};
|
|
51
|
+
export type BrowserSelectParameters = BrowserTabTarget & {
|
|
52
|
+
selector: string;
|
|
53
|
+
} & ({
|
|
54
|
+
label: string;
|
|
55
|
+
value?: never;
|
|
56
|
+
} | {
|
|
57
|
+
value: string;
|
|
58
|
+
label?: never;
|
|
59
|
+
});
|
|
51
60
|
export type BrowserScrollParameters = BrowserTabTarget & {
|
|
52
61
|
selector?: string;
|
|
53
62
|
direction?: BrowserScrollDirection;
|
|
@@ -91,6 +100,9 @@ export type BrowserProviderRequest = ProviderRequestEnvelope & ({
|
|
|
91
100
|
} | {
|
|
92
101
|
action: 'browser.type';
|
|
93
102
|
parameters: BrowserTypeParameters;
|
|
103
|
+
} | {
|
|
104
|
+
action: 'browser.select';
|
|
105
|
+
parameters: BrowserSelectParameters;
|
|
94
106
|
} | {
|
|
95
107
|
action: 'browser.scroll';
|
|
96
108
|
parameters: BrowserScrollParameters;
|
|
@@ -124,6 +136,12 @@ export type BrowserElementBounds = {
|
|
|
124
136
|
width: number;
|
|
125
137
|
height: number;
|
|
126
138
|
};
|
|
139
|
+
export type BrowserSnapshotOption = {
|
|
140
|
+
label: string;
|
|
141
|
+
value: string;
|
|
142
|
+
selected: boolean;
|
|
143
|
+
disabled: boolean;
|
|
144
|
+
};
|
|
127
145
|
/** One interactive element of a snapshot; absent fields mean empty or default. */
|
|
128
146
|
export type BrowserSnapshotElement = {
|
|
129
147
|
selector: string;
|
|
@@ -134,6 +152,9 @@ export type BrowserSnapshotElement = {
|
|
|
134
152
|
role?: string;
|
|
135
153
|
label?: string;
|
|
136
154
|
disabled?: true;
|
|
155
|
+
options?: BrowserSnapshotOption[];
|
|
156
|
+
optionsTruncated?: boolean;
|
|
157
|
+
multiple?: boolean;
|
|
137
158
|
missingAccessibleName?: true;
|
|
138
159
|
};
|
|
139
160
|
export type BrowserOpenData = {
|
|
@@ -184,6 +205,12 @@ export type BrowserTypeData = {
|
|
|
184
205
|
selector: string;
|
|
185
206
|
url: string;
|
|
186
207
|
};
|
|
208
|
+
export type BrowserSelectData = {
|
|
209
|
+
selector: string;
|
|
210
|
+
label: string;
|
|
211
|
+
value: string;
|
|
212
|
+
url: string;
|
|
213
|
+
};
|
|
187
214
|
export type BrowserScrollData = {
|
|
188
215
|
scrollY: number;
|
|
189
216
|
maxScrollY: number;
|
|
@@ -226,7 +253,7 @@ export type BrowserCloseData = {
|
|
|
226
253
|
closed: true;
|
|
227
254
|
tabId?: string;
|
|
228
255
|
};
|
|
229
|
-
export type BrowserProviderData = BrowserOpenData | BrowserSnapshotData | BrowserClickData | BrowserTypeData | BrowserScrollData | BrowserNavigationData | BrowserInspectData | BrowserCaptureData | BrowserResizeData | BrowserCloseData;
|
|
256
|
+
export type BrowserProviderData = BrowserOpenData | BrowserSnapshotData | BrowserClickData | BrowserTypeData | BrowserSelectData | BrowserScrollData | BrowserNavigationData | BrowserInspectData | BrowserCaptureData | BrowserResizeData | BrowserCloseData;
|
|
230
257
|
/**
|
|
231
258
|
* What the service answers. `error` is read by the agent, so it should say
|
|
232
259
|
* what to do differently ("No element matches #save"), not what broke inside.
|
|
@@ -247,4 +274,352 @@ export declare const isBrowserControlAction: (value: string) => value is Browser
|
|
|
247
274
|
* not a browser action; answer it with HTTP 400.
|
|
248
275
|
*/
|
|
249
276
|
export declare const readBrowserProviderRequest: (body: string) => BrowserProviderRequest | null;
|
|
277
|
+
/** Route on the service loopback OpenChamber posts every source-control call to. */
|
|
278
|
+
export declare const SOURCE_CONTROL_PROVIDER_PATH = "/source-control";
|
|
279
|
+
/** The `v` of every request; a service answers a version it does not know with HTTP 400. */
|
|
280
|
+
export declare const SOURCE_CONTROL_PROVIDER_WIRE_VERSION = 1;
|
|
281
|
+
/**
|
|
282
|
+
* Every call OpenChamber forwards. `auth.verify` reads who a candidate token
|
|
283
|
+
* belongs to and `auth.probe` whether the instance is this forge; the fifteen
|
|
284
|
+
* reads take the shell's own input without the session; the three
|
|
285
|
+
* `mutation.*` steps are the part of a write the service owns, while
|
|
286
|
+
* OpenChamber owns the kind, the normalized input, its digest, the
|
|
287
|
+
* idempotency record and when each step runs.
|
|
288
|
+
*/
|
|
289
|
+
export declare const SOURCE_CONTROL_PROVIDER_METHODS: readonly ['auth.verify', 'auth.probe', 'changeRequestStatus', 'listChangeRequests', 'changeRequestContext', 'listIssues', 'getIssue', 'issueComments', 'projectUpstream', 'projectBranches', 'listLabels', 'listReviewerCandidates', 'readChangeRequestPatch', 'readChangeRequestFile', 'liveSummaries', 'readChangeRequestHead', 'resolveChangeRequestSource', 'mutation.resolveTarget', 'mutation.perform', 'mutation.reconcile'];
|
|
290
|
+
export type SourceControlProviderMethod = (typeof SOURCE_CONTROL_PROVIDER_METHODS)[number];
|
|
291
|
+
export type SourceControlMutationMethod = Extract<SourceControlProviderMethod, `mutation.${string}`>;
|
|
292
|
+
export type SourceControlResourceMethod = Exclude<SourceControlProviderMethod, `auth.${string}` | SourceControlMutationMethod>;
|
|
293
|
+
/** `changeRequestStatus` answers within the badge's own deadline; past it the last known status shows. */
|
|
294
|
+
export declare const SOURCE_CONTROL_PROVIDER_STATUS_TIMEOUT_MS = 12000;
|
|
295
|
+
export declare const SOURCE_CONTROL_PROVIDER_READ_TIMEOUT_MS = 20000;
|
|
296
|
+
export declare const SOURCE_CONTROL_PROVIDER_MUTATION_TIMEOUT_MS = 30000;
|
|
297
|
+
export declare const SOURCE_CONTROL_PROVIDER_AUTH_TIMEOUT_MS = 15000;
|
|
298
|
+
/** Answers are read up to this many bytes, enough for a change request's patch or one file. */
|
|
299
|
+
export declare const SOURCE_CONTROL_PROVIDER_RESPONSE_MAX = 8000000;
|
|
300
|
+
/** Characters of `error.message` OpenChamber reads. */
|
|
301
|
+
export declare const SOURCE_CONTROL_PROVIDER_ERROR_MESSAGE_MAX = 2000;
|
|
302
|
+
/** No call for this long stops the service; the next call starts it again. */
|
|
303
|
+
export declare const SOURCE_CONTROL_PROVIDER_IDLE_MS: number;
|
|
304
|
+
/** Characters of a mutation's `providerTarget` JSON, which OpenChamber stores with the record. */
|
|
305
|
+
export declare const SOURCE_CONTROL_PROVIDER_TARGET_MAX = 4096;
|
|
306
|
+
/** How long OpenChamber waits for the service to answer `method`. */
|
|
307
|
+
export declare const sourceControlProviderTimeoutMs: (method: SourceControlProviderMethod) => number;
|
|
308
|
+
/**
|
|
309
|
+
* Environment variable with the base URL of OpenChamber's callback listener
|
|
310
|
+
* (`http://127.0.0.1:<port>`). Set only for a service that provides
|
|
311
|
+
* `source-control`; `createSourceControlForge` reads it.
|
|
312
|
+
*/
|
|
313
|
+
export declare const OPENCHAMBER_HOST_CALLBACK_URL = "OPENCHAMBER_HOST_CALLBACK_URL";
|
|
314
|
+
/** Route on the callback listener a service sends a forge request to. */
|
|
315
|
+
export declare const SOURCE_CONTROL_OUTBOUND_PATH = "/source-control/outbound";
|
|
316
|
+
export declare const SOURCE_CONTROL_OUTBOUND_METHODS: readonly ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
|
|
317
|
+
export type SourceControlOutboundMethod = (typeof SOURCE_CONTROL_OUTBOUND_METHODS)[number];
|
|
318
|
+
/** Request headers OpenChamber passes to the forge; it sets every other header itself. */
|
|
319
|
+
export declare const SOURCE_CONTROL_OUTBOUND_REQUEST_HEADERS: readonly ['accept', 'content-type', 'if-none-match', 'range'];
|
|
320
|
+
/** Response headers OpenChamber hands back, plus any starting with `SOURCE_CONTROL_OUTBOUND_RESPONSE_HEADER_PREFIX`. */
|
|
321
|
+
export declare const SOURCE_CONTROL_OUTBOUND_RESPONSE_HEADERS: readonly ['content-type', 'link', 'etag', 'x-total-count', 'x-total', 'retry-after'];
|
|
322
|
+
export declare const SOURCE_CONTROL_OUTBOUND_RESPONSE_HEADER_PREFIX = "x-ratelimit-";
|
|
323
|
+
/** Characters of an outbound request body. */
|
|
324
|
+
export declare const SOURCE_CONTROL_OUTBOUND_BODY_MAX = 1000000;
|
|
325
|
+
/** Bytes of a forge answer OpenChamber reads; a longer one is refused whole. */
|
|
326
|
+
export declare const SOURCE_CONTROL_OUTBOUND_RESPONSE_MAX = 8000000;
|
|
327
|
+
/** One forge request may take this long; the call's own deadline may end it sooner. */
|
|
328
|
+
export declare const SOURCE_CONTROL_OUTBOUND_TIMEOUT_MS = 20000;
|
|
329
|
+
export type SourceControlJson = null | boolean | number | string | SourceControlJson[] | {
|
|
330
|
+
[key: string]: SourceControlJson;
|
|
331
|
+
};
|
|
332
|
+
export type SourceControlJsonObject = {
|
|
333
|
+
[key: string]: SourceControlJson;
|
|
334
|
+
};
|
|
335
|
+
/** The forge account a call acts as; `null` in `auth.verify` and `auth.probe`, before one is known. */
|
|
336
|
+
export type SourceControlProviderUser = {
|
|
337
|
+
id: number;
|
|
338
|
+
login: string;
|
|
339
|
+
};
|
|
340
|
+
/**
|
|
341
|
+
* Who a call is for. `instance` is the normalized origin (`https://git.example.com`).
|
|
342
|
+
* `handle` is the capability for this one call: pass it to
|
|
343
|
+
* `createSourceControlForge(...).request`. It stops working when the call
|
|
344
|
+
* ends; never store it.
|
|
345
|
+
*/
|
|
346
|
+
export type SourceControlProviderSession = {
|
|
347
|
+
instance: string;
|
|
348
|
+
handle: string;
|
|
349
|
+
user: SourceControlProviderUser | null;
|
|
350
|
+
};
|
|
351
|
+
/**
|
|
352
|
+
* One call, as OpenChamber posts it. `provider` is the id OpenChamber gave this
|
|
353
|
+
* Git host (`ext-<panel.id>`): put it in every DTO's `provider` field.
|
|
354
|
+
* `input` was built and validated by OpenChamber; its shape per method is
|
|
355
|
+
* `SourceControlProviderInputs`.
|
|
356
|
+
*/
|
|
357
|
+
export type SourceControlProviderRequest = {
|
|
358
|
+
v: typeof SOURCE_CONTROL_PROVIDER_WIRE_VERSION;
|
|
359
|
+
requestId: string;
|
|
360
|
+
method: SourceControlProviderMethod;
|
|
361
|
+
provider: string;
|
|
362
|
+
session: SourceControlProviderSession | null;
|
|
363
|
+
input: SourceControlJsonObject;
|
|
364
|
+
};
|
|
365
|
+
export type SourceControlProjectSelector = {
|
|
366
|
+
owner: string;
|
|
367
|
+
name: string;
|
|
368
|
+
};
|
|
369
|
+
export type SourceControlProjectRef = {
|
|
370
|
+
id: string;
|
|
371
|
+
owner: string;
|
|
372
|
+
name: string;
|
|
373
|
+
};
|
|
374
|
+
/** The bound context OpenChamber validated: binding, account and the remote it reads through. */
|
|
375
|
+
type ReadInput = {
|
|
376
|
+
context: SourceControlJsonObject;
|
|
377
|
+
directory: string;
|
|
378
|
+
remote: string;
|
|
379
|
+
};
|
|
380
|
+
type ListInput = ReadInput & {
|
|
381
|
+
page: number;
|
|
382
|
+
query?: string;
|
|
383
|
+
state: string;
|
|
384
|
+
people: string;
|
|
385
|
+
};
|
|
386
|
+
/**
|
|
387
|
+
* `revision` (a 40-hex commit sha) asks for one revision of the change
|
|
388
|
+
* request instead of its current one. OpenChamber sends it only to a host
|
|
389
|
+
* that declares `capabilities.revisions`.
|
|
390
|
+
*/
|
|
391
|
+
type RevisionInput = {
|
|
392
|
+
revision?: string;
|
|
393
|
+
};
|
|
394
|
+
/**
|
|
395
|
+
* The durable target of a mutation, exactly the keys OpenChamber stores:
|
|
396
|
+
* the binding it was made under and what it acts on.
|
|
397
|
+
*/
|
|
398
|
+
export type SourceControlMutationTarget = {
|
|
399
|
+
repositoryId: string;
|
|
400
|
+
bindingRevision: number;
|
|
401
|
+
primaryRemote: string;
|
|
402
|
+
project: SourceControlProjectRef;
|
|
403
|
+
number?: number;
|
|
404
|
+
head?: string;
|
|
405
|
+
base?: string;
|
|
406
|
+
headSha?: string;
|
|
407
|
+
};
|
|
408
|
+
/** `providerTarget` is the service's own coordinates for the write (≤ `SOURCE_CONTROL_PROVIDER_TARGET_MAX` bytes of JSON). */
|
|
409
|
+
export type SourceControlMutationResolution = {
|
|
410
|
+
providerTarget: SourceControlJsonObject;
|
|
411
|
+
target: SourceControlMutationTarget;
|
|
412
|
+
};
|
|
413
|
+
/**
|
|
414
|
+
* `input` is the normalized input of the route. A `/pr/review` on a host that
|
|
415
|
+
* declares `capabilities.reviewModel: "votes"` carries `votes` (label name to
|
|
416
|
+
* score) and an optional `body` instead of `verdict`; OpenChamber sends `votes`
|
|
417
|
+
* to no other host.
|
|
418
|
+
*/
|
|
419
|
+
type MutationStepInput = {
|
|
420
|
+
route: string;
|
|
421
|
+
kind: string;
|
|
422
|
+
input: SourceControlJsonObject;
|
|
423
|
+
resolved: SourceControlMutationResolution;
|
|
424
|
+
};
|
|
425
|
+
/** What each method's `input` holds. */
|
|
426
|
+
export type SourceControlProviderInputs = {
|
|
427
|
+
'auth.verify': Record<string, never>;
|
|
428
|
+
'auth.probe': Record<string, never>;
|
|
429
|
+
changeRequestStatus: ReadInput & {
|
|
430
|
+
branch: string;
|
|
431
|
+
force: boolean;
|
|
432
|
+
};
|
|
433
|
+
listChangeRequests: ListInput;
|
|
434
|
+
changeRequestContext: ReadInput & RevisionInput & {
|
|
435
|
+
number: number;
|
|
436
|
+
project?: SourceControlProjectSelector;
|
|
437
|
+
includeDiff: boolean;
|
|
438
|
+
includeCIDetails: boolean;
|
|
439
|
+
includeTimeline: boolean;
|
|
440
|
+
};
|
|
441
|
+
listIssues: ListInput;
|
|
442
|
+
getIssue: ReadInput & {
|
|
443
|
+
number: number;
|
|
444
|
+
project?: SourceControlProjectSelector;
|
|
445
|
+
};
|
|
446
|
+
issueComments: ReadInput & {
|
|
447
|
+
number: number;
|
|
448
|
+
project?: SourceControlProjectSelector;
|
|
449
|
+
};
|
|
450
|
+
projectUpstream: ReadInput;
|
|
451
|
+
projectBranches: ReadInput & {
|
|
452
|
+
project: SourceControlProjectSelector;
|
|
453
|
+
};
|
|
454
|
+
listLabels: ReadInput & {
|
|
455
|
+
project: SourceControlProjectSelector;
|
|
456
|
+
};
|
|
457
|
+
listReviewerCandidates: ReadInput & {
|
|
458
|
+
project: SourceControlProjectSelector;
|
|
459
|
+
};
|
|
460
|
+
readChangeRequestPatch: ReadInput & RevisionInput & {
|
|
461
|
+
number: number;
|
|
462
|
+
sourceRepo?: {
|
|
463
|
+
owner: string;
|
|
464
|
+
repo: string;
|
|
465
|
+
};
|
|
466
|
+
};
|
|
467
|
+
readChangeRequestFile: ReadInput & RevisionInput & {
|
|
468
|
+
number: number;
|
|
469
|
+
sourceRepo?: {
|
|
470
|
+
owner: string;
|
|
471
|
+
repo: string;
|
|
472
|
+
};
|
|
473
|
+
path: string;
|
|
474
|
+
previousPath?: string;
|
|
475
|
+
status?: string;
|
|
476
|
+
};
|
|
477
|
+
liveSummaries: {
|
|
478
|
+
refs: Array<{
|
|
479
|
+
owner: string;
|
|
480
|
+
repo: string;
|
|
481
|
+
number: number;
|
|
482
|
+
}>;
|
|
483
|
+
issueRefs: Array<{
|
|
484
|
+
owner: string;
|
|
485
|
+
repo: string;
|
|
486
|
+
number: number;
|
|
487
|
+
}>;
|
|
488
|
+
};
|
|
489
|
+
readChangeRequestHead: ReadInput & {
|
|
490
|
+
number: number;
|
|
491
|
+
};
|
|
492
|
+
resolveChangeRequestSource: ReadInput & {
|
|
493
|
+
project: SourceControlProjectRef;
|
|
494
|
+
number: number;
|
|
495
|
+
expectedHeadSha: string;
|
|
496
|
+
};
|
|
497
|
+
'mutation.resolveTarget': {
|
|
498
|
+
context: SourceControlJsonObject;
|
|
499
|
+
kind: string;
|
|
500
|
+
input: SourceControlJsonObject;
|
|
501
|
+
replayOf?: SourceControlJsonObject;
|
|
502
|
+
};
|
|
503
|
+
'mutation.perform': MutationStepInput;
|
|
504
|
+
'mutation.reconcile': MutationStepInput;
|
|
505
|
+
};
|
|
506
|
+
export type SourceControlForgeUser = {
|
|
507
|
+
id: number;
|
|
508
|
+
login: string;
|
|
509
|
+
name?: string;
|
|
510
|
+
email?: string;
|
|
511
|
+
avatarUrl?: string;
|
|
512
|
+
};
|
|
513
|
+
export type SourceControlVerifyData = {
|
|
514
|
+
user: SourceControlForgeUser;
|
|
515
|
+
};
|
|
516
|
+
export type SourceControlProbeData = {
|
|
517
|
+
supported: boolean;
|
|
518
|
+
version?: string;
|
|
519
|
+
message?: string;
|
|
520
|
+
};
|
|
521
|
+
export type SourceControlChangeRequestHead = {
|
|
522
|
+
project: SourceControlProjectRef;
|
|
523
|
+
headSha: string;
|
|
524
|
+
headBranch: string;
|
|
525
|
+
headOwner: string;
|
|
526
|
+
};
|
|
527
|
+
/** OpenChamber adds the context and the requested remote name; the service names where the head lives. */
|
|
528
|
+
export type SourceControlChangeRequestSource = {
|
|
529
|
+
sourceProject: SourceControlProjectRef;
|
|
530
|
+
targetProject: SourceControlProjectRef;
|
|
531
|
+
classification: 'same-repository' | 'contributor-fork';
|
|
532
|
+
headSha: string;
|
|
533
|
+
headRef: string;
|
|
534
|
+
/** The head repository's HTTPS clone URL on the instance. */
|
|
535
|
+
endpoint: string;
|
|
536
|
+
};
|
|
537
|
+
/** Only these keys are stored with a finished mutation. */
|
|
538
|
+
export type SourceControlMutationResult = {
|
|
539
|
+
project?: SourceControlProjectRef;
|
|
540
|
+
number?: number;
|
|
541
|
+
head?: string;
|
|
542
|
+
base?: string;
|
|
543
|
+
headSha?: string;
|
|
544
|
+
state?: string;
|
|
545
|
+
merged?: boolean;
|
|
546
|
+
outcome?: 'queued' | 'merging';
|
|
547
|
+
stacked?: boolean;
|
|
548
|
+
ready?: boolean;
|
|
549
|
+
commented?: boolean;
|
|
550
|
+
failureStatus?: number;
|
|
551
|
+
failureCode?: string;
|
|
552
|
+
};
|
|
553
|
+
export type SourceControlReconcileData = {
|
|
554
|
+
state: 'succeeded' | 'failed' | 'outcome-unknown';
|
|
555
|
+
result?: SourceControlMutationResult;
|
|
556
|
+
};
|
|
557
|
+
/**
|
|
558
|
+
* An answer. `error.status` is the HTTP status OpenChamber should treat the
|
|
559
|
+
* failure as (a 4xx the forge gave, say); a 401 only counts when OpenChamber's
|
|
560
|
+
* proxy saw the forge answer 401 itself. `error.code` is `[A-Z_]{1,64}`.
|
|
561
|
+
*/
|
|
562
|
+
export type SourceControlProviderError = {
|
|
563
|
+
message: string;
|
|
564
|
+
status?: number;
|
|
565
|
+
code?: string;
|
|
566
|
+
};
|
|
567
|
+
export type SourceControlProviderResult = {
|
|
568
|
+
ok: true;
|
|
569
|
+
data: SourceControlJson;
|
|
570
|
+
} | {
|
|
571
|
+
ok: false;
|
|
572
|
+
error: SourceControlProviderError;
|
|
573
|
+
};
|
|
574
|
+
export declare const isSourceControlProviderMethod: (value: string) => value is SourceControlProviderMethod;
|
|
575
|
+
/**
|
|
576
|
+
* A service's read of the body OpenChamber posted to
|
|
577
|
+
* `SOURCE_CONTROL_PROVIDER_PATH`. OpenChamber built and validated `input`, so
|
|
578
|
+
* this checks the envelope, not every field, and the service bundle needs no
|
|
579
|
+
* schema library. `null` is a body that is not a source-control call of this
|
|
580
|
+
* wire version; answer it with HTTP 400.
|
|
581
|
+
*/
|
|
582
|
+
export declare const readSourceControlProviderRequest: (body: string) => SourceControlProviderRequest | null;
|
|
583
|
+
/** A request to the forge, relative to the call's instance origin. */
|
|
584
|
+
export type SourceControlOutboundRequest = {
|
|
585
|
+
method: SourceControlOutboundMethod;
|
|
586
|
+
/** Starts with `/`; stays on the instance origin. */
|
|
587
|
+
path: string;
|
|
588
|
+
query?: Record<string, string>;
|
|
589
|
+
/** Only `SOURCE_CONTROL_OUTBOUND_REQUEST_HEADERS`; OpenChamber adds the token itself. */
|
|
590
|
+
headers?: Record<string, string>;
|
|
591
|
+
/** At most `SOURCE_CONTROL_OUTBOUND_BODY_MAX` characters. */
|
|
592
|
+
body?: string;
|
|
593
|
+
};
|
|
594
|
+
/** The forge's answer as OpenChamber relayed it: any status, the allowed headers, the body as text. */
|
|
595
|
+
export type SourceControlOutboundResponse = {
|
|
596
|
+
status: number;
|
|
597
|
+
headers: Record<string, string>;
|
|
598
|
+
body: string;
|
|
599
|
+
};
|
|
600
|
+
/**
|
|
601
|
+
* OpenChamber refused the request (an expired handle, a path off the
|
|
602
|
+
* instance, a body too large), could not reach the forge, or did not answer.
|
|
603
|
+
* `status` is the callback listener's HTTP status, `0` when it gave none;
|
|
604
|
+
* `code` is its error code (`HOST_UNAVAILABLE` when there was no usable answer).
|
|
605
|
+
*/
|
|
606
|
+
export declare class SourceControlForgeError extends Error {
|
|
607
|
+
readonly code: string;
|
|
608
|
+
readonly status: number;
|
|
609
|
+
constructor(message: string, code: string, status: number);
|
|
610
|
+
}
|
|
611
|
+
export type SourceControlForge = {
|
|
612
|
+
/** Sends `request` to the forge of the call `handle` belongs to, with the stored token when the call has one. */
|
|
613
|
+
request(handle: string, request: SourceControlOutboundRequest): Promise<SourceControlOutboundResponse>;
|
|
614
|
+
};
|
|
615
|
+
/**
|
|
616
|
+
* The service's way to the forge: OpenChamber's callback listener
|
|
617
|
+
* (`OPENCHAMBER_HOST_CALLBACK_URL`), which checks the handle, keeps the request
|
|
618
|
+
* on the instance origin and adds the token. Plain `fetch`, no dependencies.
|
|
619
|
+
* Throws `SourceControlForgeError` when OpenChamber refuses the request; a
|
|
620
|
+
* forge answer of any status, 401 included, resolves.
|
|
621
|
+
*/
|
|
622
|
+
export declare const createSourceControlForge: ({ env }: {
|
|
623
|
+
env: Record<string, string | undefined>;
|
|
624
|
+
}) => SourceControlForge;
|
|
250
625
|
export {};
|