@alpacakit/integrations 0.1.0-beta.21 → 0.1.0-beta.25
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/README.md +7 -0
- package/dist/catalog.d.ts +12 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +82 -0
- package/dist/code-hosts/account.d.ts.map +1 -1
- package/dist/code-hosts/account.js +4 -1
- package/dist/code-hosts/facets.d.ts +2 -1
- package/dist/code-hosts/facets.d.ts.map +1 -1
- package/dist/code-hosts/facets.js +65 -12
- package/dist/code-hosts/gitea-pull-request-snapshot.d.ts +22 -0
- package/dist/code-hosts/gitea-pull-request-snapshot.d.ts.map +1 -0
- package/dist/code-hosts/gitea-pull-request-snapshot.js +226 -0
- package/dist/code-hosts/gitea.d.ts.map +1 -1
- package/dist/code-hosts/gitea.js +50 -49
- package/dist/code-hosts/github.d.ts.map +1 -1
- package/dist/code-hosts/github.js +34 -6
- package/dist/code-hosts/gitlab.d.ts.map +1 -1
- package/dist/code-hosts/gitlab.js +32 -8
- package/dist/code-hosts/helpers.js +4 -4
- package/dist/code-hosts/normalize.d.ts +1 -0
- package/dist/code-hosts/normalize.d.ts.map +1 -1
- package/dist/code-hosts/normalize.js +1 -1
- package/dist/code-hosts/schemas.d.ts +134 -0
- package/dist/code-hosts/schemas.d.ts.map +1 -1
- package/dist/code-hosts/schemas.js +31 -2
- package/dist/code-hosts/strategy.d.ts +9 -2
- package/dist/code-hosts/strategy.d.ts.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/store.d.ts +2 -2
- package/dist/config/store.d.ts.map +1 -1
- package/dist/config/store.js +79 -1
- package/dist/definition.d.ts +34 -1
- package/dist/definition.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/keyring/index.d.ts +1 -1
- package/dist/keyring/index.d.ts.map +1 -1
- package/dist/keyring/resolver.d.ts +1 -0
- package/dist/keyring/resolver.d.ts.map +1 -1
- package/dist/keyring/resolver.js +14 -18
- package/dist/model.d.ts +48 -8
- package/dist/model.d.ts.map +1 -1
- package/dist/runtime.d.ts +6 -4
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +3 -0
- package/dist/testing/contracts.d.ts.map +1 -1
- package/dist/testing/contracts.js +0 -1
- package/dist/values.d.ts +29 -0
- package/dist/values.d.ts.map +1 -1
- package/dist/values.js +29 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,3 +4,10 @@ Provider-neutral, import-only TypeScript contracts and HTTP adapters for
|
|
|
4
4
|
development-management services. The root export includes the runtime and the
|
|
5
5
|
built-in GitHub, GitLab, and Gitea definitions. Machine-local config, native
|
|
6
6
|
keyring support, and test fakes are isolated under subpath exports.
|
|
7
|
+
|
|
8
|
+
Repository setup is split into two provider-owned contracts:
|
|
9
|
+
`RepositoryScopeAdapter` applies provider-neutral repository updates to typed
|
|
10
|
+
connection settings and projects them back to a repository reference, while
|
|
11
|
+
the connection-level `RepositoryHost` finds or creates repositories.
|
|
12
|
+
Repository-scoped issue, pull-request, and file operations remain on
|
|
13
|
+
`Repository`.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type JsonObject } from "@alpacakit/core";
|
|
2
|
+
import type { IntegrationDefinition, IntegrationSettingsInput } from "./definition.js";
|
|
3
|
+
import type { IntegrationId } from "./ids.js";
|
|
4
|
+
import type { RepositoryRef } from "./model.js";
|
|
5
|
+
type AnyDefinition = IntegrationDefinition<IntegrationId, any, any>;
|
|
6
|
+
export interface IntegrationDefinitionCatalog {
|
|
7
|
+
resolveSettings(integration: IntegrationId, current: JsonObject | null, input: IntegrationSettingsInput): JsonObject;
|
|
8
|
+
projectRepository(integration: IntegrationId, settings: JsonObject): RepositoryRef;
|
|
9
|
+
}
|
|
10
|
+
export declare function createIntegrationDefinitionCatalog(definitions: readonly AnyDefinition[]): IntegrationDefinitionCatalog;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,KAAK,EACV,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAOhD,KAAK,aAAa,GAAG,qBAAqB,CAAC,aAAa,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAEpE,MAAM,WAAW,4BAA4B;IAC3C,eAAe,CACb,WAAW,EAAE,aAAa,EAC1B,OAAO,EAAE,UAAU,GAAG,IAAI,EAC1B,KAAK,EAAE,wBAAwB,GAC9B,UAAU,CAAC;IACd,iBAAiB,CACf,WAAW,EAAE,aAAa,EAC1B,QAAQ,EAAE,UAAU,GACnB,aAAa,CAAC;CAClB;AAED,wBAAgB,kCAAkC,CAChD,WAAW,EAAE,SAAS,aAAa,EAAE,GACpC,4BAA4B,CAE9B"}
|
package/dist/catalog.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { CODEC_DECODE_RESULT_KIND } from "@alpacakit/core";
|
|
2
|
+
import { INTEGRATION_ERROR_CODE, IntegrationError } from "./error.js";
|
|
3
|
+
import { INTEGRATION_SETTINGS_INPUT_KIND, REPOSITORY_SETTINGS_UPDATE_KIND, } from "./values.js";
|
|
4
|
+
export function createIntegrationDefinitionCatalog(definitions) {
|
|
5
|
+
return new DefaultIntegrationDefinitionCatalog(definitions);
|
|
6
|
+
}
|
|
7
|
+
class DefaultIntegrationDefinitionCatalog {
|
|
8
|
+
definitions;
|
|
9
|
+
constructor(definitions) {
|
|
10
|
+
const indexed = new Map();
|
|
11
|
+
for (const definition of definitions) {
|
|
12
|
+
if (indexed.has(definition.id)) {
|
|
13
|
+
throw invalidInput(`Duplicate integration id "${definition.id}"`, `integration id "${definition.id}" registered twice`, definition.id);
|
|
14
|
+
}
|
|
15
|
+
indexed.set(definition.id, definition);
|
|
16
|
+
}
|
|
17
|
+
this.definitions = indexed;
|
|
18
|
+
}
|
|
19
|
+
resolveSettings(id, current, input) {
|
|
20
|
+
const definition = this.requireDefinition(id);
|
|
21
|
+
if (input.kind === INTEGRATION_SETTINGS_INPUT_KIND.raw) {
|
|
22
|
+
this.decodeSettings(definition, input.settings);
|
|
23
|
+
return input.settings;
|
|
24
|
+
}
|
|
25
|
+
const adapter = definition.repositoryScope;
|
|
26
|
+
if (adapter === undefined) {
|
|
27
|
+
throw unsupported(definition.id, "repository settings");
|
|
28
|
+
}
|
|
29
|
+
const currentSettings = current === null ? null : this.decodeSettings(definition, current);
|
|
30
|
+
if (input.update.kind === REPOSITORY_SETTINGS_UPDATE_KIND.baseUrl) {
|
|
31
|
+
if (currentSettings === null) {
|
|
32
|
+
throw invalidInput(`Repository settings are required for integration "${definition.id}"`, "a base URL cannot identify a repository by itself", definition.id);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const settings = adapter.applySettings({
|
|
36
|
+
current: currentSettings,
|
|
37
|
+
...input.update,
|
|
38
|
+
});
|
|
39
|
+
this.decodeSettings(definition, settings);
|
|
40
|
+
return settings;
|
|
41
|
+
}
|
|
42
|
+
projectRepository(id, settings) {
|
|
43
|
+
const definition = this.requireDefinition(id);
|
|
44
|
+
const adapter = definition.repositoryScope;
|
|
45
|
+
if (adapter === undefined) {
|
|
46
|
+
throw unsupported(definition.id, "repository settings");
|
|
47
|
+
}
|
|
48
|
+
return adapter.projectRepository(this.decodeSettings(definition, settings));
|
|
49
|
+
}
|
|
50
|
+
requireDefinition(id) {
|
|
51
|
+
const definition = this.definitions.get(id);
|
|
52
|
+
if (definition === undefined) {
|
|
53
|
+
throw invalidInput(`Unknown integration id "${id}"`, `no integration registered with id "${id}"`, id);
|
|
54
|
+
}
|
|
55
|
+
return definition;
|
|
56
|
+
}
|
|
57
|
+
decodeSettings(definition, settings) {
|
|
58
|
+
const decoded = definition.settingsCodec.decode(settings);
|
|
59
|
+
if (decoded.kind === CODEC_DECODE_RESULT_KIND.invalid) {
|
|
60
|
+
throw invalidInput(`Invalid settings for integration "${definition.id}"`, decoded.issues.join("; "), definition.id);
|
|
61
|
+
}
|
|
62
|
+
return decoded.value;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function invalidInput(message, reason, integrationId) {
|
|
66
|
+
return new IntegrationError({
|
|
67
|
+
code: INTEGRATION_ERROR_CODE.invalidInput,
|
|
68
|
+
message,
|
|
69
|
+
data: { reason },
|
|
70
|
+
integrationId,
|
|
71
|
+
connectionId: null,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function unsupported(integrationId, operation) {
|
|
75
|
+
return new IntegrationError({
|
|
76
|
+
code: INTEGRATION_ERROR_CODE.unsupported,
|
|
77
|
+
message: `Integration "${integrationId}" does not support ${operation}`,
|
|
78
|
+
data: { operation },
|
|
79
|
+
integrationId,
|
|
80
|
+
connectionId: null,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../src/code-hosts/account.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,OAAO,EAEP,cAAc,EAEf,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,cAAc,CAAC;AAStB,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAEvE;
|
|
1
|
+
{"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../src/code-hosts/account.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,OAAO,EAEP,cAAc,EAEf,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,cAAc,CAAC;AAStB,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAEvE;AAyGD,wBAAgB,WAAW,CACzB,UAAU,EAAE,aAAa,EACzB,QAAQ,EAAE,cAAc,CAAC,WAAW,CAAC,GACpC,cAAc,CAAC,WAAW,CAAC,CAO7B"}
|
|
@@ -67,7 +67,10 @@ class CodeHostAccount {
|
|
|
67
67
|
if (this.repositoryRequest !== null) {
|
|
68
68
|
return this.repositoryRequest;
|
|
69
69
|
}
|
|
70
|
-
const pending = this.session.transport.request(this.session.strategy.paths.repository(
|
|
70
|
+
const pending = this.session.transport.request(this.session.strategy.paths.repository({
|
|
71
|
+
owner: this.session.strategy.scope.owner,
|
|
72
|
+
name: this.session.strategy.scope.repository,
|
|
73
|
+
}), rawRepositoryCodec);
|
|
71
74
|
this.repositoryRequest = pending;
|
|
72
75
|
void pending.catch(() => {
|
|
73
76
|
if (this.repositoryRequest === pending) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DoctorCheck } from "../login.js";
|
|
2
|
-
import type { Repository, WorkItems } from "../model.js";
|
|
2
|
+
import type { Repository, RepositoryHost, WorkItems } from "../model.js";
|
|
3
3
|
import type { HttpTransport } from "../transport/http-transport.js";
|
|
4
4
|
import { type CodeHostStrategy } from "./strategy.js";
|
|
5
5
|
export { createCodeHostAccount } from "./account.js";
|
|
@@ -9,5 +9,6 @@ export type CodeHostSession = {
|
|
|
9
9
|
};
|
|
10
10
|
export declare function createCodeHostWorkItems(session: CodeHostSession): WorkItems;
|
|
11
11
|
export declare function createCodeHostRepository(session: CodeHostSession): Repository;
|
|
12
|
+
export declare function createCodeHostRepositoryHost(session: CodeHostSession): RepositoryHost;
|
|
12
13
|
export declare function probeCodeHostDoctor(session: CodeHostSession): Promise<DoctorCheck[]>;
|
|
13
14
|
//# sourceMappingURL=facets.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facets.d.ts","sourceRoot":"","sources":["../../src/code-hosts/facets.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"facets.d.ts","sourceRoot":"","sources":["../../src/code-hosts/facets.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAQV,UAAU,EACV,cAAc,EAKd,SAAS,EACV,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAuCpE,OAAO,EAEL,KAAK,gBAAgB,EAGtB,MAAM,eAAe,CAAC;AAavB,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAqBrD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;CACrC,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,CA0I3E;AAiKD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,eAAe,GAAG,UAAU,CAoE7E;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,eAAe,GACvB,cAAc,CAwChB;AA0ID,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,WAAW,EAAE,CAAC,CAgExB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { INTEGRATION_ERROR_CODE, IntegrationError, isIntegrationError, withIntegrationIdentity, } from "../error.js";
|
|
2
2
|
import { commentReaction, } from "../ids.js";
|
|
3
3
|
import { collectPaginated } from "../transport/collect.js";
|
|
4
|
-
import { DOCTOR_CHECK_STATUS, WORK_ITEM_KIND, WORK_ITEM_QUERY_STATE, WORK_ITEM_STATE, } from "../values.js";
|
|
4
|
+
import { DOCTOR_CHECK_STATUS, ENSURE_REPOSITORY_RESULT_KIND, PULL_REQUEST_REVIEW_CONTEXT_AVAILABILITY, WORK_ITEM_KIND, WORK_ITEM_QUERY_STATE, WORK_ITEM_STATE, } from "../values.js";
|
|
5
5
|
import { ownerKindOf } from "./account.js";
|
|
6
6
|
import { commentIdentity } from "./helpers.js";
|
|
7
7
|
import { normalizeComment, normalizeCommit, normalizePullRequest, normalizeWorkItem, } from "./normalize.js";
|
|
@@ -220,7 +220,7 @@ export function createCodeHostRepository(session) {
|
|
|
220
220
|
const ensureLinkedBranch = strategy.ensureLinkedBranch;
|
|
221
221
|
return {
|
|
222
222
|
info: async () => {
|
|
223
|
-
const raw = await transport.request(strategy.paths.repository(), rawRepositoryCodec);
|
|
223
|
+
const raw = await transport.request(strategy.paths.repository(repository), rawRepositoryCodec);
|
|
224
224
|
return {
|
|
225
225
|
ref: repository,
|
|
226
226
|
defaultBranch: raw.defaultBranch,
|
|
@@ -258,17 +258,37 @@ export function createCodeHostRepository(session) {
|
|
|
258
258
|
: {
|
|
259
259
|
ensure: async (workItem, branch) => await ensureLinkedBranch(transport, refNumber(workItem), branch),
|
|
260
260
|
},
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
export function createCodeHostRepositoryHost(session) {
|
|
264
|
+
const { transport, strategy } = session;
|
|
265
|
+
const normalizeRepository = (raw, ref) => ({
|
|
266
|
+
ref,
|
|
267
|
+
defaultBranch: raw.defaultBranch,
|
|
268
|
+
url: raw.url,
|
|
269
|
+
isPrivate: raw.isPrivate,
|
|
270
|
+
ownerKind: ownerKindOf(raw, strategy.ownerKind),
|
|
271
|
+
});
|
|
272
|
+
const find = async (ref) => {
|
|
273
|
+
const raw = await transport.requestOptional(strategy.paths.repository(ref), rawRepositoryCodec);
|
|
274
|
+
return raw === null ? null : normalizeRepository(raw, ref);
|
|
275
|
+
};
|
|
276
|
+
return {
|
|
277
|
+
find,
|
|
278
|
+
ensure: async (input) => {
|
|
279
|
+
const { ref } = input;
|
|
280
|
+
const existing = await find(ref);
|
|
281
|
+
if (existing !== null) {
|
|
264
282
|
return {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
url: raw.url,
|
|
268
|
-
isPrivate: raw.isPrivate,
|
|
269
|
-
ownerKind: ownerKindOf(raw, strategy.ownerKind),
|
|
283
|
+
kind: ENSURE_REPOSITORY_RESULT_KIND.existing,
|
|
284
|
+
repository: existing,
|
|
270
285
|
};
|
|
271
|
-
}
|
|
286
|
+
}
|
|
287
|
+
const raw = await transport.request(await strategy.provisionRequest(transport, input), rawRepositoryCodec);
|
|
288
|
+
return {
|
|
289
|
+
kind: ENSURE_REPOSITORY_RESULT_KIND.created,
|
|
290
|
+
repository: normalizeRepository(raw, ref),
|
|
291
|
+
};
|
|
272
292
|
},
|
|
273
293
|
};
|
|
274
294
|
}
|
|
@@ -291,6 +311,39 @@ function createPullRequests(session, normalizePr, getByRef) {
|
|
|
291
311
|
changedFiles: (ref) => collectPaginated(transport, strategy.paths.pullRequests.changedFiles(refNumber(ref)), rawChangedFileListCodec, strategy.pager),
|
|
292
312
|
commits: async (ref) => (await collectPaginated(transport, strategy.paths.pullRequests.commits(refNumber(ref)), rawCommitListCodec, strategy.pager)).map(normalizeCommit),
|
|
293
313
|
status: async (ref) => await strategy.pullRequestStatus(transport, refNumber(ref)),
|
|
314
|
+
snapshot: async (ref) => {
|
|
315
|
+
const read = strategy.pullRequestSnapshot;
|
|
316
|
+
if (read !== undefined) {
|
|
317
|
+
const snapshot = await read(transport, refNumber(ref));
|
|
318
|
+
return snapshot === null
|
|
319
|
+
? null
|
|
320
|
+
: {
|
|
321
|
+
pullRequest: normalizePr(snapshot.pullRequest),
|
|
322
|
+
status: snapshot.status,
|
|
323
|
+
reviewContext: snapshot.reviewContext,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
const raw = await getByRef(ref);
|
|
327
|
+
if (raw === null) {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
const status = await strategy.pullRequestStatus(transport, refNumber(ref));
|
|
331
|
+
const pullRequest = normalizePr(raw);
|
|
332
|
+
return {
|
|
333
|
+
pullRequest,
|
|
334
|
+
status: status ?? {
|
|
335
|
+
reviewDecision: null,
|
|
336
|
+
mergeability: null,
|
|
337
|
+
mergeStateStatus: null,
|
|
338
|
+
isDraft: pullRequest.isDraft,
|
|
339
|
+
headSha: pullRequest.headSha,
|
|
340
|
+
latestApprovedReviewSha: null,
|
|
341
|
+
},
|
|
342
|
+
reviewContext: {
|
|
343
|
+
availability: PULL_REQUEST_REVIEW_CONTEXT_AVAILABILITY.unsupported,
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
},
|
|
294
347
|
headIncludedInBase: async (ref) => {
|
|
295
348
|
const pr = await getByRef(ref);
|
|
296
349
|
if (pr === null) {
|
|
@@ -331,7 +384,7 @@ function createPullRequests(session, normalizePr, getByRef) {
|
|
|
331
384
|
export async function probeCodeHostDoctor(session) {
|
|
332
385
|
const checks = [];
|
|
333
386
|
try {
|
|
334
|
-
await session.transport.request(session.strategy.paths.repository(), rawRepositoryCodec);
|
|
387
|
+
await session.transport.request(session.strategy.paths.repository(normalizationRepository(session.strategy)), rawRepositoryCodec);
|
|
335
388
|
checks.push({
|
|
336
389
|
id: DOCTOR_CHECK_ID.reachable,
|
|
337
390
|
status: DOCTOR_CHECK_STATUS.ok,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PullRequestSnapshot, PullRequestStatus } from "../model.js";
|
|
2
|
+
import type { HttpTransport } from "../transport/http-transport.js";
|
|
3
|
+
import { type RawGiteaReview, type RawPullRequest } from "./schemas.js";
|
|
4
|
+
export declare function readGiteaPullRequestSnapshot(input: {
|
|
5
|
+
readonly transport: HttpTransport;
|
|
6
|
+
readonly repositoryPath: string;
|
|
7
|
+
readonly number: string;
|
|
8
|
+
readonly pager: import("../transport/types.js").Pager;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
readonly pullRequest: RawPullRequest;
|
|
11
|
+
readonly status: PullRequestStatus;
|
|
12
|
+
readonly reviewContext: PullRequestSnapshot["reviewContext"];
|
|
13
|
+
} | null>;
|
|
14
|
+
export declare function readGiteaPullRequest(transport: HttpTransport, repositoryPath: string, number: string): Promise<RawPullRequest | null>;
|
|
15
|
+
export declare function readGiteaPullRequestReviews(input: {
|
|
16
|
+
readonly transport: HttpTransport;
|
|
17
|
+
readonly repositoryPath: string;
|
|
18
|
+
readonly number: string;
|
|
19
|
+
readonly pager: import("../transport/types.js").Pager;
|
|
20
|
+
}): Promise<RawGiteaReview[]>;
|
|
21
|
+
export declare function projectGiteaPullRequestStatus(pullRequest: RawPullRequest, reviews: readonly RawGiteaReview[]): PullRequestStatus;
|
|
22
|
+
//# sourceMappingURL=gitea-pull-request-snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitea-pull-request-snapshot.d.ts","sourceRoot":"","sources":["../../src/code-hosts/gitea-pull-request-snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAapE,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,cAAc,EAKpB,MAAM,cAAc,CAAC;AAatB,wBAAsB,4BAA4B,CAAC,KAAK,EAAE;IACxD,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,uBAAuB,EAAE,KAAK,CAAC;CACvD,GAAG,OAAO,CAAC;IACV,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,QAAQ,CAAC,aAAa,EAAE,mBAAmB,CAAC,eAAe,CAAC,CAAC;CAC9D,GAAG,IAAI,CAAC,CAsCR;AAED,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,aAAa,EACxB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAShC;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE;IACjD,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC;IAClC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,OAAO,uBAAuB,EAAE,KAAK,CAAC;CACvD,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAY5B;AAED,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,cAAc,EAC3B,OAAO,EAAE,SAAS,cAAc,EAAE,GACjC,iBAAiB,CAmCnB"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { collectPaginated } from "../transport/collect.js";
|
|
2
|
+
import { COMMENT_SIDE, HTTP_METHOD, MERGE_STATE_STATUS, MERGEABILITY, PULL_REQUEST_FEEDBACK_KIND, PULL_REQUEST_REVIEW_CONTEXT_AVAILABILITY, PULL_REQUEST_REVIEW_STATE, PULL_REQUEST_STATE, REVIEW_DECISION, } from "../values.js";
|
|
3
|
+
import { isBot, normalizeUser } from "./normalize.js";
|
|
4
|
+
import { rawCommentListCodec, rawGiteaReviewCommentListCodec, rawGiteaReviewListCodec, rawPullRequestCodec, } from "./schemas.js";
|
|
5
|
+
import { CODE_HOST_RESOURCE } from "./values.js";
|
|
6
|
+
const MAX_PAGE_SIZE = 50;
|
|
7
|
+
const INLINE_REVIEW_READ_CONCURRENCY = 4;
|
|
8
|
+
const GITEA_REVIEW_STATE = {
|
|
9
|
+
approved: "APPROVED",
|
|
10
|
+
changesRequested: "REQUEST_CHANGES",
|
|
11
|
+
commented: "COMMENT",
|
|
12
|
+
pending: "PENDING",
|
|
13
|
+
requestReview: "REQUEST_REVIEW",
|
|
14
|
+
};
|
|
15
|
+
export async function readGiteaPullRequestSnapshot(input) {
|
|
16
|
+
const pullRequest = await readGiteaPullRequest(input.transport, input.repositoryPath, input.number);
|
|
17
|
+
if (pullRequest === null) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
if (pullRequest.state.toLowerCase() !== PULL_REQUEST_STATE.open) {
|
|
21
|
+
return {
|
|
22
|
+
pullRequest,
|
|
23
|
+
status: projectGiteaPullRequestStatusWithoutReviewHistory(pullRequest),
|
|
24
|
+
reviewContext: {
|
|
25
|
+
availability: PULL_REQUEST_REVIEW_CONTEXT_AVAILABILITY.unavailable,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const [reviews, conversation] = await Promise.all([
|
|
30
|
+
readGiteaPullRequestReviews(input),
|
|
31
|
+
readGiteaPullRequestConversation(input),
|
|
32
|
+
]);
|
|
33
|
+
const inline = await readGiteaPullRequestInlineComments({
|
|
34
|
+
...input,
|
|
35
|
+
reviews,
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
pullRequest,
|
|
39
|
+
status: projectGiteaPullRequestStatus(pullRequest, reviews),
|
|
40
|
+
reviewContext: {
|
|
41
|
+
availability: PULL_REQUEST_REVIEW_CONTEXT_AVAILABILITY.available,
|
|
42
|
+
feedback: projectGiteaPullRequestFeedback({
|
|
43
|
+
conversation,
|
|
44
|
+
reviews,
|
|
45
|
+
inline,
|
|
46
|
+
}),
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function readGiteaPullRequest(transport, repositoryPath, number) {
|
|
51
|
+
return transport.requestOptional({
|
|
52
|
+
method: HTTP_METHOD.get,
|
|
53
|
+
path: `${repositoryPath}/pulls/${number}`,
|
|
54
|
+
resource: CODE_HOST_RESOURCE.pullRequest,
|
|
55
|
+
}, rawPullRequestCodec);
|
|
56
|
+
}
|
|
57
|
+
export function readGiteaPullRequestReviews(input) {
|
|
58
|
+
return collectPaginated(input.transport, {
|
|
59
|
+
method: HTTP_METHOD.get,
|
|
60
|
+
path: `${input.repositoryPath}/pulls/${input.number}/reviews`,
|
|
61
|
+
query: { limit: MAX_PAGE_SIZE },
|
|
62
|
+
resource: CODE_HOST_RESOURCE.pullRequestReviews,
|
|
63
|
+
}, rawGiteaReviewListCodec, input.pager);
|
|
64
|
+
}
|
|
65
|
+
export function projectGiteaPullRequestStatus(pullRequest, reviews) {
|
|
66
|
+
const recentFirst = reviews
|
|
67
|
+
.filter((review) => review.dismissed !== true && review.stale !== true)
|
|
68
|
+
.reverse();
|
|
69
|
+
const latestDecision = recentFirst.find((review) => review.state === GITEA_REVIEW_STATE.changesRequested ||
|
|
70
|
+
review.state === GITEA_REVIEW_STATE.approved);
|
|
71
|
+
const approved = recentFirst.find((review) => review.state === GITEA_REVIEW_STATE.approved);
|
|
72
|
+
return {
|
|
73
|
+
reviewDecision: latestDecision?.state === GITEA_REVIEW_STATE.approved
|
|
74
|
+
? REVIEW_DECISION.approved
|
|
75
|
+
: latestDecision?.state === GITEA_REVIEW_STATE.changesRequested
|
|
76
|
+
? REVIEW_DECISION.changesRequested
|
|
77
|
+
: REVIEW_DECISION.reviewRequired,
|
|
78
|
+
mergeability: pullRequest.mergeable === true
|
|
79
|
+
? MERGEABILITY.mergeable
|
|
80
|
+
: pullRequest.mergeable === false
|
|
81
|
+
? MERGEABILITY.conflicting
|
|
82
|
+
: MERGEABILITY.unknown,
|
|
83
|
+
mergeStateStatus: pullRequest.mergeable === true
|
|
84
|
+
? MERGE_STATE_STATUS.clean
|
|
85
|
+
: pullRequest.mergeable === false
|
|
86
|
+
? MERGE_STATE_STATUS.dirty
|
|
87
|
+
: MERGE_STATE_STATUS.unknown,
|
|
88
|
+
isDraft: pullRequest.isDraft,
|
|
89
|
+
headSha: pullRequest.headSha,
|
|
90
|
+
latestApprovedReviewSha: approved?.commit_id ?? null,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function projectGiteaPullRequestStatusWithoutReviewHistory(pullRequest) {
|
|
94
|
+
return {
|
|
95
|
+
...projectGiteaPullRequestStatus(pullRequest, []),
|
|
96
|
+
reviewDecision: null,
|
|
97
|
+
latestApprovedReviewSha: null,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function readGiteaPullRequestConversation(input) {
|
|
101
|
+
return collectPaginated(input.transport, {
|
|
102
|
+
method: HTTP_METHOD.get,
|
|
103
|
+
path: `${input.repositoryPath}/issues/${input.number}/comments`,
|
|
104
|
+
query: { limit: MAX_PAGE_SIZE },
|
|
105
|
+
resource: CODE_HOST_RESOURCE.comments,
|
|
106
|
+
}, rawCommentListCodec, input.pager);
|
|
107
|
+
}
|
|
108
|
+
async function readGiteaPullRequestInlineComments(input) {
|
|
109
|
+
const reviews = input.reviews.filter((review) => isSubmittedGiteaReview(review) && (review.comments_count ?? 0) > 0);
|
|
110
|
+
const comments = [];
|
|
111
|
+
for (let index = 0; index < reviews.length; index += INLINE_REVIEW_READ_CONCURRENCY) {
|
|
112
|
+
comments.push(...(await Promise.all(reviews
|
|
113
|
+
.slice(index, index + INLINE_REVIEW_READ_CONCURRENCY)
|
|
114
|
+
.map((review) => collectPaginated(input.transport, {
|
|
115
|
+
method: HTTP_METHOD.get,
|
|
116
|
+
path: `${input.repositoryPath}/pulls/${input.number}/reviews/${review.id}/comments`,
|
|
117
|
+
query: { limit: MAX_PAGE_SIZE },
|
|
118
|
+
resource: CODE_HOST_RESOURCE.pullRequestReviewComments,
|
|
119
|
+
}, rawGiteaReviewCommentListCodec, input.pager)))));
|
|
120
|
+
}
|
|
121
|
+
return comments.flatMap((page, index) => {
|
|
122
|
+
const review = reviews[index];
|
|
123
|
+
return review === undefined
|
|
124
|
+
? []
|
|
125
|
+
: page.map((comment) => ({ review, comment }));
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function projectGiteaPullRequestFeedback(input) {
|
|
129
|
+
return [
|
|
130
|
+
...input.conversation.map(projectGiteaConversationFeedback),
|
|
131
|
+
...input.reviews
|
|
132
|
+
.filter(isSubmittedGiteaReview)
|
|
133
|
+
.filter((review) => (review.body ?? "").trim().length > 0)
|
|
134
|
+
.map(projectGiteaReviewFeedback),
|
|
135
|
+
...input.inline.map(projectGiteaInlineFeedback),
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
function projectGiteaConversationFeedback(comment) {
|
|
139
|
+
return {
|
|
140
|
+
id: `${PULL_REQUEST_FEEDBACK_KIND.conversation}:${comment.id}`,
|
|
141
|
+
kind: PULL_REQUEST_FEEDBACK_KIND.conversation,
|
|
142
|
+
body: comment.body,
|
|
143
|
+
url: comment.url,
|
|
144
|
+
author: normalizeUser(comment.author),
|
|
145
|
+
isBot: isBot(comment.author),
|
|
146
|
+
createdAt: comment.createdAt,
|
|
147
|
+
updatedAt: comment.updatedAt,
|
|
148
|
+
reviewState: null,
|
|
149
|
+
commitSha: null,
|
|
150
|
+
stale: false,
|
|
151
|
+
dismissed: false,
|
|
152
|
+
resolved: false,
|
|
153
|
+
path: null,
|
|
154
|
+
line: null,
|
|
155
|
+
side: null,
|
|
156
|
+
inReplyTo: null,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function projectGiteaReviewFeedback(review) {
|
|
160
|
+
return {
|
|
161
|
+
id: `${PULL_REQUEST_FEEDBACK_KIND.review}:${review.id}`,
|
|
162
|
+
kind: PULL_REQUEST_FEEDBACK_KIND.review,
|
|
163
|
+
body: review.body ?? "",
|
|
164
|
+
url: review.html_url ?? null,
|
|
165
|
+
author: review.user == null ? null : normalizeUser(review.user),
|
|
166
|
+
isBot: review.user == null ? false : isBot(review.user),
|
|
167
|
+
createdAt: review.submitted_at ?? null,
|
|
168
|
+
updatedAt: review.updated_at ?? null,
|
|
169
|
+
reviewState: normalizeGiteaReviewState(review),
|
|
170
|
+
commitSha: review.commit_id ?? null,
|
|
171
|
+
stale: review.stale ?? false,
|
|
172
|
+
dismissed: review.dismissed ?? false,
|
|
173
|
+
resolved: false,
|
|
174
|
+
path: null,
|
|
175
|
+
line: null,
|
|
176
|
+
side: null,
|
|
177
|
+
inReplyTo: null,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function projectGiteaInlineFeedback(input) {
|
|
181
|
+
const position = input.comment.position ?? 0;
|
|
182
|
+
const originalPosition = input.comment.original_position ?? 0;
|
|
183
|
+
return {
|
|
184
|
+
id: `${PULL_REQUEST_FEEDBACK_KIND.inline}:${input.comment.id}`,
|
|
185
|
+
kind: PULL_REQUEST_FEEDBACK_KIND.inline,
|
|
186
|
+
body: input.comment.body ?? "",
|
|
187
|
+
url: input.comment.html_url ?? null,
|
|
188
|
+
author: input.comment.user == null ? null : normalizeUser(input.comment.user),
|
|
189
|
+
isBot: input.comment.user == null ? false : isBot(input.comment.user),
|
|
190
|
+
createdAt: input.comment.created_at ?? null,
|
|
191
|
+
updatedAt: input.comment.updated_at ?? null,
|
|
192
|
+
reviewState: normalizeGiteaReviewState(input.review),
|
|
193
|
+
commitSha: input.comment.commit_id ?? input.review.commit_id ?? null,
|
|
194
|
+
stale: input.review.stale ?? false,
|
|
195
|
+
dismissed: input.review.dismissed ?? false,
|
|
196
|
+
resolved: input.comment.resolver != null,
|
|
197
|
+
path: input.comment.path ?? null,
|
|
198
|
+
line: position > 0 ? position : originalPosition > 0 ? originalPosition : null,
|
|
199
|
+
side: position > 0
|
|
200
|
+
? COMMENT_SIDE.right
|
|
201
|
+
: originalPosition > 0
|
|
202
|
+
? COMMENT_SIDE.left
|
|
203
|
+
: null,
|
|
204
|
+
inReplyTo: input.comment.in_reply_to_id == null
|
|
205
|
+
? null
|
|
206
|
+
: `${PULL_REQUEST_FEEDBACK_KIND.inline}:${input.comment.in_reply_to_id}`,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function isSubmittedGiteaReview(review) {
|
|
210
|
+
return (review.state !== GITEA_REVIEW_STATE.pending &&
|
|
211
|
+
review.state !== GITEA_REVIEW_STATE.requestReview);
|
|
212
|
+
}
|
|
213
|
+
function normalizeGiteaReviewState(review) {
|
|
214
|
+
if (review.dismissed === true) {
|
|
215
|
+
return PULL_REQUEST_REVIEW_STATE.dismissed;
|
|
216
|
+
}
|
|
217
|
+
if (review.state === GITEA_REVIEW_STATE.approved) {
|
|
218
|
+
return PULL_REQUEST_REVIEW_STATE.approved;
|
|
219
|
+
}
|
|
220
|
+
if (review.state === GITEA_REVIEW_STATE.changesRequested) {
|
|
221
|
+
return PULL_REQUEST_REVIEW_STATE.changesRequested;
|
|
222
|
+
}
|
|
223
|
+
return review.state === GITEA_REVIEW_STATE.commented
|
|
224
|
+
? PULL_REQUEST_REVIEW_STATE.commented
|
|
225
|
+
: PULL_REQUEST_REVIEW_STATE.unknown;
|
|
226
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gitea.d.ts","sourceRoot":"","sources":["../../src/code-hosts/gitea.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gitea.d.ts","sourceRoot":"","sources":["../../src/code-hosts/gitea.ts"],"names":[],"mappings":"AAiCA,OAAO,EACL,KAAK,eAAe,EAMrB,MAAM,aAAa,CAAC;AA2BrB,eAAO,MAAM,oBAAoB,2DAAyB,CAAC;AAO3D,eAAO,MAAM,oBAAoB,6DAGvB,CAAC;AACX,eAAO,MAAM,2BAA2B;;;CAGA,CAAC;AAEzC,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAwBF,wBAAgB,KAAK,gJAuDpB"}
|