@alpacakit/integrations 0.1.0-beta.31 → 0.1.0-beta.36

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.
Files changed (52) hide show
  1. package/README.md +6 -2
  2. package/dist/auth.d.ts +2 -2
  3. package/dist/auth.d.ts.map +1 -1
  4. package/dist/auth.js +9 -1
  5. package/dist/catalog.d.ts +3 -3
  6. package/dist/catalog.d.ts.map +1 -1
  7. package/dist/catalog.js +10 -3
  8. package/dist/code-hosts/facets.d.ts +2 -2
  9. package/dist/code-hosts/facets.d.ts.map +1 -1
  10. package/dist/code-hosts/facets.js +4 -48
  11. package/dist/code-hosts/gitea-pull-request-snapshot.js +0 -1
  12. package/dist/code-hosts/gitea.d.ts.map +1 -1
  13. package/dist/code-hosts/gitea.js +100 -12
  14. package/dist/code-hosts/github.d.ts.map +1 -1
  15. package/dist/code-hosts/github.js +71 -21
  16. package/dist/code-hosts/gitlab.d.ts.map +1 -1
  17. package/dist/code-hosts/gitlab.js +70 -18
  18. package/dist/code-hosts/helpers.d.ts +12 -3
  19. package/dist/code-hosts/helpers.d.ts.map +1 -1
  20. package/dist/code-hosts/helpers.js +67 -7
  21. package/dist/code-hosts/repository-host.d.ts +4 -0
  22. package/dist/code-hosts/repository-host.d.ts.map +1 -0
  23. package/dist/code-hosts/repository-host.js +61 -0
  24. package/dist/code-hosts/repository-info.d.ts +4 -0
  25. package/dist/code-hosts/repository-info.d.ts.map +1 -0
  26. package/dist/code-hosts/repository-info.js +11 -0
  27. package/dist/code-hosts/strategy.d.ts +3 -2
  28. package/dist/code-hosts/strategy.d.ts.map +1 -1
  29. package/dist/definition.d.ts +18 -3
  30. package/dist/definition.d.ts.map +1 -1
  31. package/dist/definition.js +5 -0
  32. package/dist/error.d.ts +1 -0
  33. package/dist/error.d.ts.map +1 -1
  34. package/dist/index.d.ts +5 -5
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +3 -3
  37. package/dist/model.d.ts +10 -8
  38. package/dist/model.d.ts.map +1 -1
  39. package/dist/runtime.d.ts +12 -3
  40. package/dist/runtime.d.ts.map +1 -1
  41. package/dist/runtime.js +25 -4
  42. package/dist/testing/contracts.d.ts +13 -2
  43. package/dist/testing/contracts.d.ts.map +1 -1
  44. package/dist/testing/contracts.js +108 -1
  45. package/dist/testing/index.d.ts +1 -1
  46. package/dist/testing/index.d.ts.map +1 -1
  47. package/dist/testing/index.js +1 -1
  48. package/dist/transport/http-transport.js +1 -1
  49. package/dist/values.d.ts +13 -2
  50. package/dist/values.d.ts.map +1 -1
  51. package/dist/values.js +13 -2
  52. package/package.json +2 -2
package/README.md CHANGED
@@ -7,8 +7,12 @@ keyring support, and test fakes are isolated under subpath exports.
7
7
 
8
8
  Repository setup is split into two provider-owned contracts:
9
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.
10
+ connection settings and projects them back to one `RepositoryScope` containing
11
+ the repository reference and canonical provider base URL, while the
12
+ connection-level `RepositoryHost` finds repositories or exclusively creates a
13
+ new private repository without adopting a name conflict. Repository facts
14
+ include the provider's opaque immutable repository ID alongside the canonical
15
+ reference and URL.
12
16
  Repository-scoped issue, pull-request, and file operations remain on
13
17
  `Repository`.
14
18
 
package/dist/auth.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import type { SecretRef, ValueOf } from "@alpacakit/core";
2
2
  import type { IntegrationAuthProfileId, IntegrationId } from "./ids.js";
3
3
  import type { SecretResolvers, SessionPorts } from "./ports.js";
4
- import type { INTEGRATION_AUTH_BINDING_KIND } from "./values.js";
5
- import { AUTHORIZATION_KIND, INTEGRATION_AUTH_MATERIAL_KIND } from "./values.js";
4
+ import { AUTHORIZATION_KIND, INTEGRATION_AUTH_BINDING_KIND, INTEGRATION_AUTH_MATERIAL_KIND } from "./values.js";
6
5
  export type { SecretRef } from "@alpacakit/core";
7
6
  export type IntegrationAuthMaterial = {
8
7
  readonly kind: typeof INTEGRATION_AUTH_MATERIAL_KIND.apiKey;
@@ -31,6 +30,7 @@ export type IntegrationAuthBinding = {
31
30
  } | {
32
31
  readonly kind: typeof INTEGRATION_AUTH_BINDING_KIND.anonymous;
33
32
  };
33
+ export declare function sameIntegrationAuthBinding(left: IntegrationAuthBinding, right: IntegrationAuthBinding): boolean;
34
34
  export type IntegrationAuthProfile = {
35
35
  readonly id: IntegrationAuthProfileId;
36
36
  readonly integrationId: IntegrationId;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
package/dist/auth.js CHANGED
@@ -1,7 +1,15 @@
1
1
  import { AUTH_FAILURE_REASON, INTEGRATION_ERROR_CODE, IntegrationError, } from "./error.js";
2
- import { AUTHORIZATION_KIND, AUTHORIZATION_SCHEME, INTEGRATION_AUTH_MATERIAL_KIND, } from "./values.js";
2
+ import { AUTHORIZATION_KIND, AUTHORIZATION_SCHEME, INTEGRATION_AUTH_BINDING_KIND, INTEGRATION_AUTH_MATERIAL_KIND, } from "./values.js";
3
3
  const UTF8_ENCODING = "utf8";
4
4
  const BASE64_ENCODING = "base64";
5
+ export function sameIntegrationAuthBinding(left, right) {
6
+ if (left.kind !== right.kind)
7
+ return false;
8
+ return left.kind !== INTEGRATION_AUTH_BINDING_KIND.profile
9
+ ? true
10
+ : right.kind === INTEGRATION_AUTH_BINDING_KIND.profile &&
11
+ left.profileId === right.profileId;
12
+ }
5
13
  export async function requireSecret(resolvers, ref) {
6
14
  const resolver = resolvers[ref.resolver];
7
15
  if (resolver === undefined) {
package/dist/catalog.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { type JsonObject } from "@alpacakit/core";
2
- import type { IntegrationDefinition, IntegrationSettingsInput } from "./definition.js";
2
+ import type { IntegrationDefinition, IntegrationSettingsInput, RepositoryRemoteResolution, RepositoryScope } from "./definition.js";
3
3
  import type { IntegrationId } from "./ids.js";
4
- import type { RepositoryRef } from "./model.js";
5
4
  type AnyDefinition = IntegrationDefinition<IntegrationId, any, any>;
6
5
  export interface IntegrationDefinitionCatalog {
7
6
  resolveSettings(integration: IntegrationId, current: JsonObject | null, input: IntegrationSettingsInput): JsonObject;
8
- projectRepository(integration: IntegrationId, settings: JsonObject): RepositoryRef;
7
+ projectRepositoryScope(integration: IntegrationId, settings: JsonObject): RepositoryScope;
8
+ resolveRepositoryRemote(integration: IntegrationId, remoteUrl: string): RepositoryRemoteResolution;
9
9
  }
10
10
  export declare function createIntegrationDefinitionCatalog(definitions: readonly AnyDefinition[]): IntegrationDefinitionCatalog;
11
11
  export {};
@@ -1 +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"}
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,EACxB,0BAA0B,EAC1B,eAAe,EAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAQ9C,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,sBAAsB,CACpB,WAAW,EAAE,aAAa,EAC1B,QAAQ,EAAE,UAAU,GACnB,eAAe,CAAC;IACnB,uBAAuB,CACrB,WAAW,EAAE,aAAa,EAC1B,SAAS,EAAE,MAAM,GAChB,0BAA0B,CAAC;CAC/B;AAED,wBAAgB,kCAAkC,CAChD,WAAW,EAAE,SAAS,aAAa,EAAE,GACpC,4BAA4B,CAE9B"}
package/dist/catalog.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { CODEC_DECODE_RESULT_KIND } from "@alpacakit/core";
2
2
  import { INTEGRATION_ERROR_CODE, IntegrationError } from "./error.js";
3
- import { INTEGRATION_SETTINGS_INPUT_KIND, REPOSITORY_SETTINGS_UPDATE_KIND, } from "./values.js";
3
+ import { INTEGRATION_SETTINGS_INPUT_KIND, REPOSITORY_REMOTE_RESOLUTION_KIND, REPOSITORY_SETTINGS_UPDATE_KIND, } from "./values.js";
4
4
  export function createIntegrationDefinitionCatalog(definitions) {
5
5
  return new DefaultIntegrationDefinitionCatalog(definitions);
6
6
  }
@@ -39,13 +39,20 @@ class DefaultIntegrationDefinitionCatalog {
39
39
  this.decodeSettings(definition, settings);
40
40
  return settings;
41
41
  }
42
- projectRepository(id, settings) {
42
+ projectRepositoryScope(id, settings) {
43
43
  const definition = this.requireDefinition(id);
44
44
  const adapter = definition.repositoryScope;
45
45
  if (adapter === undefined) {
46
46
  throw unsupported(definition.id, "repository settings");
47
47
  }
48
- return adapter.projectRepository(this.decodeSettings(definition, settings));
48
+ return adapter.project(this.decodeSettings(definition, settings));
49
+ }
50
+ resolveRepositoryRemote(id, remoteUrl) {
51
+ const definition = this.requireDefinition(id);
52
+ const adapter = definition.repositoryScope;
53
+ return adapter === undefined
54
+ ? { kind: REPOSITORY_REMOTE_RESOLUTION_KIND.unmatched }
55
+ : adapter.resolveRemote(remoteUrl);
49
56
  }
50
57
  requireDefinition(id) {
51
58
  const definition = this.definitions.get(id);
@@ -1,14 +1,14 @@
1
1
  import type { DoctorCheck } from "../login.js";
2
- import type { Repository, RepositoryHost, WorkItems } from "../model.js";
2
+ import type { Repository, 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";
6
+ export { createCodeHostRepositoryHost } from "./repository-host.js";
6
7
  export type CodeHostSession = {
7
8
  readonly transport: HttpTransport;
8
9
  readonly strategy: CodeHostStrategy;
9
10
  };
10
11
  export declare function createCodeHostWorkItems(session: CodeHostSession): WorkItems;
11
12
  export declare function createCodeHostRepository(session: CodeHostSession): Repository;
12
- export declare function createCodeHostRepositoryHost(session: CodeHostSession): RepositoryHost;
13
13
  export declare function probeCodeHostDoctor(session: CodeHostSession): Promise<DoctorCheck[]>;
14
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,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
+ {"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,EAKV,SAAS,EACV,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAqCpE,OAAO,EAEL,KAAK,gBAAgB,EAGtB,MAAM,eAAe,CAAC;AAavB,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAkBpE,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,CA8D7E;AA0ID,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,WAAW,EAAE,CAAC,CA2DxB"}
@@ -1,10 +1,10 @@
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, ENSURE_REPOSITORY_RESULT_KIND, PULL_REQUEST_REVIEW_CONTEXT_AVAILABILITY, WORK_ITEM_KIND, WORK_ITEM_QUERY_STATE, WORK_ITEM_STATE, } from "../values.js";
5
- import { ownerKindOf } from "./account.js";
4
+ import { DOCTOR_CHECK_STATUS, PULL_REQUEST_REVIEW_CONTEXT_AVAILABILITY, WORK_ITEM_KIND, WORK_ITEM_QUERY_STATE, WORK_ITEM_STATE, } from "../values.js";
6
5
  import { commentIdentity } from "./helpers.js";
7
6
  import { normalizeComment, normalizeCommit, normalizePullRequest, normalizeWorkItem, } from "./normalize.js";
7
+ import { normalizeRepositoryInfo } from "./repository-info.js";
8
8
  import { rawChangedFileListCodec, rawCommentCodec, rawCommentListCodec, rawCommitListCodec, rawCompareCodec, rawFileCodec, rawLabelCodec, rawLabelListCodec, rawMilestoneListCodec, rawPullRequestCodec, rawPullRequestListCodec, rawReactionListCodec, rawRepositoryCodec, rawViewerCodec, rawWorkItemCodec, rawWorkItemListCodec, rawWorkItemSearchCodec, } from "./schemas.js";
9
9
  import { CODE_HOST_CHANGE_FEED_KIND, refNumber, } from "./strategy.js";
10
10
  import { advanceTimestampChangeFeed, timestampChangeFeedBeginningPosition, timestampChangeFeedPositionAt, timestampChangeFeedQueryBoundary, } from "./timestamp-change-feed.js";
@@ -13,14 +13,13 @@ import { CODE_HOST_LABEL_KIND } from "./values.js";
13
13
  // in ./account.js; re-exported here so the provider strategies keep a single
14
14
  // facets entry point.
15
15
  export { createCodeHostAccount } from "./account.js";
16
+ export { createCodeHostRepositoryHost } from "./repository-host.js";
16
17
  const DOCTOR_CHECK_ID = {
17
18
  authenticated: "authenticated",
18
19
  reachable: "reachable",
19
- scopes: "scopes",
20
20
  scopeAccess: "scope-access",
21
21
  };
22
22
  const REQUEST_FAILED_DETAIL = "request failed";
23
- const SCOPES_UNAVAILABLE_DETAIL = "The host did not expose token scopes in the probe response.";
24
23
  const COMPARE_STATUS = {
25
24
  identical: "identical",
26
25
  behind: "behind",
@@ -221,13 +220,7 @@ export function createCodeHostRepository(session) {
221
220
  return {
222
221
  info: async () => {
223
222
  const raw = await transport.request(strategy.paths.repository(repository), rawRepositoryCodec);
224
- return {
225
- ref: repository,
226
- defaultBranch: raw.defaultBranch,
227
- url: raw.url,
228
- isPrivate: raw.isPrivate,
229
- ownerKind: ownerKindOf(raw, strategy.ownerKind),
230
- };
223
+ return normalizeRepositoryInfo(raw, repository, strategy.ownerKind);
231
224
  },
232
225
  pullRequests: createPullRequests(session, normalizePr, getByRef),
233
226
  files: {
@@ -260,38 +253,6 @@ export function createCodeHostRepository(session) {
260
253
  },
261
254
  };
262
255
  }
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) {
282
- return {
283
- kind: ENSURE_REPOSITORY_RESULT_KIND.existing,
284
- repository: existing,
285
- };
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
- };
292
- },
293
- };
294
- }
295
256
  function createPullRequests(session, normalizePr, getByRef) {
296
257
  const { transport, strategy } = session;
297
258
  return {
@@ -425,11 +386,6 @@ export async function probeCodeHostDoctor(session) {
425
386
  detail: error instanceof Error ? error.message : REQUEST_FAILED_DETAIL,
426
387
  });
427
388
  }
428
- checks.push({
429
- id: DOCTOR_CHECK_ID.scopes,
430
- status: DOCTOR_CHECK_STATUS.warn,
431
- detail: SCOPES_UNAVAILABLE_DETAIL,
432
- });
433
389
  return checks;
434
390
  }
435
391
  function createReactions(session) {
@@ -130,7 +130,6 @@ function projectGiteaPullRequestFeedback(input) {
130
130
  ...input.conversation.map(projectGiteaConversationFeedback),
131
131
  ...input.reviews
132
132
  .filter(isSubmittedGiteaReview)
133
- .filter((review) => (review.body ?? "").trim().length > 0)
134
133
  .map(projectGiteaReviewFeedback),
135
134
  ...input.inline.map(projectGiteaInlineFeedback),
136
135
  ];
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"gitea.d.ts","sourceRoot":"","sources":["../../src/code-hosts/gitea.ts"],"names":[],"mappings":"AAqCA,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;AAiIF,wBAAgB,KAAK,gJAqDpB"}
@@ -2,16 +2,16 @@
2
2
  import { createSchemaRuntimeCodec } from "@alpacakit/core";
3
3
  import { z } from "zod";
4
4
  import { bearerMaterialHandlers } from "../auth.js";
5
- import { defineIntegration } from "../definition.js";
5
+ import { defineIntegration, } from "../definition.js";
6
6
  import { INTEGRATION_ERROR_CODE, IntegrationError, isIntegrationError, } from "../error.js";
7
7
  import { integrationId } from "../ids.js";
8
8
  import { collectPaginated } from "../transport/collect.js";
9
9
  import { HttpTransport } from "../transport/http-transport.js";
10
10
  import { LinkHeaderPager } from "../transport/pager.js";
11
- import { AUTHORIZATION_SCHEME, GIT_PROTOCOL, HTTP_METHOD, MERGE_METHOD, OWNER_KIND, PULL_REQUEST_STATE, REPOSITORY_SETTINGS_UPDATE_KIND, WORK_ITEM_KIND, WORK_ITEM_QUERY_STATE, WORK_ITEM_STATE, } from "../values.js";
11
+ import { AUTHORIZATION_SCHEME, GIT_PROTOCOL, HTTP_METHOD, MERGE_METHOD, OWNER_KIND, PULL_REQUEST_STATE, REPOSITORY_REMOTE_RESOLUTION_KIND, REPOSITORY_SETTINGS_UPDATE_KIND, WORK_ITEM_KIND, WORK_ITEM_QUERY_STATE, WORK_ITEM_STATE, } from "../values.js";
12
12
  import { createCodeHostAccount, createCodeHostRepository, createCodeHostRepositoryHost, createCodeHostWorkItems, probeCodeHostDoctor, } from "./facets.js";
13
13
  import { projectGiteaPullRequestStatus, readGiteaPullRequest, readGiteaPullRequestReviews, readGiteaPullRequestSnapshot, } from "./gitea-pull-request-snapshot.js";
14
- import { ambientToken, matchesRemote, orgOrUserRepositoryRequest, } from "./helpers.js";
14
+ import { ambientToken, hasRepositoryNameConflictDetail, orgOrUserRepositoryRequest, } from "./helpers.js";
15
15
  import { normalizeDependencyIssue } from "./normalize.js";
16
16
  import { rawGiteaDependencyListCodec, rawGiteaLabelCodec, rawGiteaLabelListCodec, } from "./schemas.js";
17
17
  import { CODE_HOST_CHANGE_FEED_KIND, refNumber } from "./strategy.js";
@@ -50,12 +50,92 @@ const settingsSchema = z
50
50
  baseUrl: settings.baseUrl.replace(/\/$/, ""),
51
51
  }));
52
52
  const cursorCodec = createTimestampChangeFeedCursorCodec(GITEA_INTEGRATION_ID);
53
+ function resolveGiteaRemote(remoteUrl) {
54
+ const parsed = parseCanonicalHttpUrl(remoteUrl);
55
+ if (parsed.kind === REPOSITORY_REMOTE_RESOLUTION_KIND.rejected)
56
+ return parsed;
57
+ if (parsed.segments.length < 2) {
58
+ return rejectedRemote("Gitea remote path must end with an owner and repository.");
59
+ }
60
+ const repositorySegment = parsed.segments.at(-1);
61
+ const repository = repositorySegment.endsWith(".git")
62
+ ? repositorySegment.slice(0, -4)
63
+ : repositorySegment;
64
+ const owner = parsed.segments.at(-2);
65
+ if (repository.length === 0) {
66
+ return rejectedRemote("Gitea remote repository must not be empty.");
67
+ }
68
+ const installationPath = parsed.segments.slice(0, -2);
69
+ return {
70
+ kind: REPOSITORY_REMOTE_RESOLUTION_KIND.matched,
71
+ repository: { owner, name: repository },
72
+ baseUrl: canonicalHttpUrl(parsed.origin, installationPath),
73
+ };
74
+ }
75
+ function parseCanonicalHttpUrl(remoteUrl) {
76
+ let url;
77
+ try {
78
+ url = new URL(remoteUrl);
79
+ }
80
+ catch {
81
+ return rejectedRemote("Gitea remote must be an absolute HTTP(S) URL.");
82
+ }
83
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
84
+ return rejectedRemote("Gitea remote must use HTTP or HTTPS.");
85
+ }
86
+ if (url.username !== "" ||
87
+ url.password !== "" ||
88
+ url.search !== "" ||
89
+ url.hash !== "") {
90
+ return rejectedRemote("Gitea remote must not contain credentials, a query, or a fragment.");
91
+ }
92
+ const encodedSegments = url.pathname === "/" ? [] : url.pathname.split("/").slice(1);
93
+ if (encodedSegments.some((segment) => segment.length === 0)) {
94
+ return rejectedRemote("Gitea remote path contains an empty segment.");
95
+ }
96
+ const segments = [];
97
+ for (const encoded of encodedSegments) {
98
+ let segment;
99
+ try {
100
+ segment = decodeURIComponent(encoded);
101
+ }
102
+ catch {
103
+ return rejectedRemote("Gitea remote path contains invalid encoding.");
104
+ }
105
+ if (segment === "." ||
106
+ segment === ".." ||
107
+ segment.includes("/") ||
108
+ segment.includes("\\")) {
109
+ return rejectedRemote("Gitea remote path contains an unsafe segment.");
110
+ }
111
+ segments.push(segment);
112
+ }
113
+ return {
114
+ kind: REPOSITORY_REMOTE_RESOLUTION_KIND.matched,
115
+ origin: url.origin,
116
+ segments,
117
+ };
118
+ }
119
+ function canonicalHttpUrl(origin, segments) {
120
+ return segments.length === 0
121
+ ? origin
122
+ : `${origin}/${segments.map((segment) => encodeURIComponent(segment)).join("/")}`;
123
+ }
124
+ function canonicalGiteaBaseUrl(baseUrl) {
125
+ const parsed = parseCanonicalHttpUrl(baseUrl);
126
+ return parsed.kind === REPOSITORY_REMOTE_RESOLUTION_KIND.rejected
127
+ ? null
128
+ : canonicalHttpUrl(parsed.origin, parsed.segments);
129
+ }
130
+ function rejectedRemote(detail) {
131
+ return { kind: REPOSITORY_REMOTE_RESOLUTION_KIND.rejected, detail };
132
+ }
53
133
  export function gitea() {
54
134
  return defineIntegration({
55
135
  id: GITEA_INTEGRATION_ID,
56
136
  settingsCodec: createSchemaRuntimeCodec(settingsSchema),
57
- remoteMatch: (remoteUrl, settings) => matchesRemote(remoteUrl, new URL(settings.baseUrl).hostname, `${settings.owner}/${settings.repository}`),
58
137
  repositoryScope: {
138
+ resolveRemote: resolveGiteaRemote,
59
139
  applySettings: (input) => {
60
140
  if (input.kind === REPOSITORY_SETTINGS_UPDATE_KIND.baseUrl) {
61
141
  return {
@@ -71,9 +151,12 @@ export function gitea() {
71
151
  ...(baseUrl === undefined ? {} : { baseUrl }),
72
152
  };
73
153
  },
74
- projectRepository: (settings) => ({
75
- owner: settings.owner,
76
- name: settings.repository,
154
+ project: (settings) => ({
155
+ repository: {
156
+ owner: settings.owner,
157
+ name: settings.repository,
158
+ },
159
+ baseUrl: canonicalGiteaBaseUrl(settings.baseUrl) ?? settings.baseUrl,
77
160
  }),
78
161
  },
79
162
  auth: {
@@ -154,7 +237,8 @@ function giteaStrategy(settings) {
154
237
  number,
155
238
  pager: GITEA_PAGER,
156
239
  }),
157
- provisionRequest: orgOrUserRepositoryRequest,
240
+ createNewRequest: orgOrUserRepositoryRequest,
241
+ isCreateNewConflict: isGiteaRepositoryCreateConflict,
158
242
  workItemRelations: (transport) => createGiteaRelations(transport, repositoryPath, settings),
159
243
  workItemCreateRequest: async (transport, input) => ({
160
244
  method: HTTP_METHOD.post,
@@ -338,11 +422,9 @@ function giteaStrategy(settings) {
338
422
  resource: CODE_HOST_RESOURCE.authenticatedUser,
339
423
  }),
340
424
  membership: {
341
- list: (ownerKind) => ({
425
+ list: () => ({
342
426
  method: HTTP_METHOD.get,
343
- path: ownerKind === OWNER_KIND.organization
344
- ? `/orgs/${encodeURIComponent(settings.owner)}/members`
345
- : `${repositoryPath}/collaborators`,
427
+ path: `${repositoryPath}/collaborators`,
346
428
  query: { limit: MAX_PAGE_SIZE },
347
429
  resource: CODE_HOST_RESOURCE.membership,
348
430
  }),
@@ -481,6 +563,12 @@ async function loadPalette(transport, repositoryPath) {
481
563
  resource: CODE_HOST_RESOURCE.labels,
482
564
  }, rawGiteaLabelListCodec, GITEA_PAGER);
483
565
  }
566
+ function isGiteaRepositoryCreateConflict(error) {
567
+ return (isIntegrationError(error, INTEGRATION_ERROR_CODE.conflict) &&
568
+ (error.data.status === 409 ||
569
+ (error.data.status === 422 &&
570
+ hasRepositoryNameConflictDetail(error.data.detail))));
571
+ }
484
572
  function createGiteaRelations(transport, repositoryPath, settings) {
485
573
  return {
486
574
  listBlockedBy: async (ref) => (await collectPaginated(transport, {
@@ -1 +1 @@
1
- {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/code-hosts/github.ts"],"names":[],"mappings":"AAkCA,OAAO,EACL,KAAK,eAAe,EAMrB,MAAM,aAAa,CAAC;AAuBrB,eAAO,MAAM,qBAAqB,4DAA0B,CAAC;AA8B7D,eAAO,MAAM,qBAAqB,qCAAsC,CAAC;AACzE,eAAO,MAAM,4BAA4B;;;CAGD,CAAC;AAEzC,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AA0IF,wBAAgB,MAAM,kJAkErB"}
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/code-hosts/github.ts"],"names":[],"mappings":"AA0CA,OAAO,EACL,KAAK,eAAe,EAMrB,MAAM,aAAa,CAAC;AAwBrB,eAAO,MAAM,qBAAqB,4DAA0B,CAAC;AA8B7D,eAAO,MAAM,qBAAqB,qCAAsC,CAAC;AACzE,eAAO,MAAM,4BAA4B;;;CAGD,CAAC;AAEzC,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AA6LF,wBAAgB,MAAM,kJA4DrB"}
@@ -2,16 +2,16 @@
2
2
  import { createSchemaRuntimeCodec } from "@alpacakit/core";
3
3
  import { z } from "zod";
4
4
  import { bearerMaterialHandlers } from "../auth.js";
5
- import { defineIntegration } from "../definition.js";
6
- import { INTEGRATION_ERROR_CODE, IntegrationError } from "../error.js";
5
+ import { defineIntegration, } from "../definition.js";
6
+ import { INTEGRATION_ERROR_CODE, IntegrationError, isIntegrationError, } from "../error.js";
7
7
  import { integrationId } from "../ids.js";
8
8
  import { collectPaginated } from "../transport/collect.js";
9
9
  import { HttpTransport } from "../transport/http-transport.js";
10
10
  import { LinkHeaderPager } from "../transport/pager.js";
11
- import { AUTHORIZATION_SCHEME, COMMENT_KIND, GIT_PROTOCOL, HTTP_METHOD, MERGE_METHOD, MERGE_STATE_STATUS, MERGEABILITY, OWNER_KIND, PULL_REQUEST_STATE, REPOSITORY_SETTINGS_UPDATE_KIND, REVIEW_DECISION, WORK_ITEM_QUERY_STATE, WORK_ITEM_STATE, } from "../values.js";
11
+ import { AUTHORIZATION_SCHEME, COMMENT_KIND, GIT_PROTOCOL, HTTP_METHOD, MERGE_METHOD, MERGE_STATE_STATUS, MERGEABILITY, OWNER_KIND, PULL_REQUEST_STATE, REPOSITORY_REMOTE_RESOLUTION_KIND, REPOSITORY_SETTINGS_UPDATE_KIND, REVIEW_DECISION, WORK_ITEM_QUERY_STATE, WORK_ITEM_STATE, } from "../values.js";
12
12
  import { createCodeHostAccount, createCodeHostRepository, createCodeHostRepositoryHost, createCodeHostWorkItems, probeCodeHostDoctor, } from "./facets.js";
13
13
  import { githubDeviceLogin } from "./github-login.js";
14
- import { ambientToken, matchesRemote, orgOrUserRepositoryRequest, } from "./helpers.js";
14
+ import { ambientToken, hasRepositoryNameConflictDetail, orgOrUserRepositoryRequest, parseGitRemote, } from "./helpers.js";
15
15
  import { normalizeDependencyIssue } from "./normalize.js";
16
16
  import { rawDependencyIssueListCodec, rawPullRequestCodec, rawReactionListCodec, rawWorkItemCodec, } from "./schemas.js";
17
17
  import { CODE_HOST_CHANGE_FEED_KIND, refNumber } from "./strategy.js";
@@ -65,7 +65,7 @@ const githubSettingsSchema = z
65
65
  .transform((settings) => ({
66
66
  owner: settings.owner,
67
67
  repository: settings.repository,
68
- baseUrl: settings.baseUrl ?? GITHUB_API_URL,
68
+ baseUrl: (settings.baseUrl ?? GITHUB_API_URL).replace(/\/$/, ""),
69
69
  }));
70
70
  const cursorCodec = createTimestampChangeFeedCursorCodec(GITHUB_INTEGRATION_ID);
71
71
  const githubPullRequestStatusCodec = createSchemaRuntimeCodec(z
@@ -165,18 +165,59 @@ const githubLinkedBranchMutationCodec = createSchemaRuntimeCodec(z
165
165
  .strip(),
166
166
  })
167
167
  .strip());
168
+ function resolveGitHubBaseUrls(input) {
169
+ if ("webOrigin" in input) {
170
+ return input.webOrigin === "https://github.com"
171
+ ? { webOrigin: input.webOrigin, apiBaseUrl: GITHUB_API_URL }
172
+ : {
173
+ webOrigin: input.webOrigin,
174
+ apiBaseUrl: `${input.webOrigin}/api/v3`,
175
+ };
176
+ }
177
+ const apiBaseUrl = new URL(input.apiBaseUrl).toString().replace(/\/$/u, "");
178
+ return apiBaseUrl === GITHUB_API_URL
179
+ ? { webOrigin: "https://github.com", apiBaseUrl }
180
+ : {
181
+ webOrigin: apiBaseUrl.replace(/\/api\/v3\/?$/u, ""),
182
+ apiBaseUrl,
183
+ };
184
+ }
185
+ function resolveGitHubRemote(remoteUrl) {
186
+ const parsed = parseGitRemote(remoteUrl);
187
+ if (parsed.kind === "rejected") {
188
+ return {
189
+ kind: REPOSITORY_REMOTE_RESOLUTION_KIND.rejected,
190
+ detail: `GitHub remote ${parsed.detail}.`,
191
+ };
192
+ }
193
+ if (parsed.pathSegments.length !== 2) {
194
+ return {
195
+ kind: REPOSITORY_REMOTE_RESOLUTION_KIND.rejected,
196
+ detail: "GitHub remote path must contain an owner and repository.",
197
+ };
198
+ }
199
+ const [owner, repositorySegment] = parsed.pathSegments;
200
+ const repository = repositorySegment.endsWith(".git")
201
+ ? repositorySegment.slice(0, -4)
202
+ : repositorySegment;
203
+ if (repository === "") {
204
+ return {
205
+ kind: REPOSITORY_REMOTE_RESOLUTION_KIND.rejected,
206
+ detail: "GitHub remote repository must not be empty.",
207
+ };
208
+ }
209
+ return {
210
+ kind: REPOSITORY_REMOTE_RESOLUTION_KIND.matched,
211
+ repository: { owner, name: repository },
212
+ baseUrl: resolveGitHubBaseUrls({ webOrigin: parsed.webOrigin }).apiBaseUrl,
213
+ };
214
+ }
168
215
  export function github() {
169
216
  return defineIntegration({
170
217
  id: GITHUB_INTEGRATION_ID,
171
218
  settingsCodec: createSchemaRuntimeCodec(githubSettingsSchema),
172
- remoteMatch: (remoteUrl, settings) => {
173
- const base = new URL(settings.baseUrl);
174
- const host = base.hostname === "api.github.com"
175
- ? "github.com"
176
- : base.hostname.replace(/^api\./, "");
177
- return matchesRemote(remoteUrl, host, `${settings.owner}/${settings.repository}`);
178
- },
179
219
  repositoryScope: {
220
+ resolveRemote: resolveGitHubRemote,
180
221
  applySettings: (input) => {
181
222
  if (input.kind === REPOSITORY_SETTINGS_UPDATE_KIND.baseUrl) {
182
223
  return {
@@ -185,16 +226,20 @@ export function github() {
185
226
  baseUrl: input.baseUrl,
186
227
  };
187
228
  }
188
- const baseUrl = input.baseUrl ?? input.current?.baseUrl;
229
+ const baseUrl = input.baseUrl ?? input.current?.baseUrl ?? GITHUB_API_URL;
189
230
  return {
190
231
  owner: input.repository.owner,
191
232
  repository: input.repository.name,
192
- ...(baseUrl === undefined ? {} : { baseUrl }),
233
+ baseUrl,
193
234
  };
194
235
  },
195
- projectRepository: (settings) => ({
196
- owner: settings.owner,
197
- name: settings.repository,
236
+ project: (settings) => ({
237
+ repository: {
238
+ owner: settings.owner,
239
+ name: settings.repository,
240
+ },
241
+ baseUrl: resolveGitHubBaseUrls({ apiBaseUrl: settings.baseUrl })
242
+ .apiBaseUrl,
198
243
  }),
199
244
  },
200
245
  auth: {
@@ -240,9 +285,7 @@ function createGitHubSession(settings, authorization, ports, connectionId) {
240
285
  // biome-ignore lint/complexity/noExcessiveLinesPerFunction: The provider request map stays together so endpoint behavior remains reviewable as one strategy.
241
286
  function githubStrategy(settings) {
242
287
  const repositoryPath = `/repos/${encodeURIComponent(settings.owner)}/${encodeURIComponent(settings.repository)}`;
243
- const webBaseUrl = settings.baseUrl === GITHUB_API_URL
244
- ? `https://github.com/${settings.owner}/${settings.repository}`
245
- : `${settings.baseUrl.replace(/\/api\/v3\/?$/, "")}/${settings.owner}/${settings.repository}`;
288
+ const webBaseUrl = `${resolveGitHubBaseUrls({ apiBaseUrl: settings.baseUrl }).webOrigin}/${settings.owner}/${settings.repository}`;
246
289
  const issueBody = (input) => ({
247
290
  ...(input.title === undefined ? {} : { title: input.title }),
248
291
  ...(input.body === undefined ? {} : { body: input.body }),
@@ -314,7 +357,8 @@ function githubStrategy(settings) {
314
357
  resource: CODE_HOST_RESOURCE.labels,
315
358
  }),
316
359
  },
317
- provisionRequest: orgOrUserRepositoryRequest,
360
+ createNewRequest: orgOrUserRepositoryRequest,
361
+ isCreateNewConflict: isGitHubRepositoryCreateConflict,
318
362
  workItemCreateRequest: async (_transport, input) => ({
319
363
  method: HTTP_METHOD.post,
320
364
  path: `${repositoryPath}/issues`,
@@ -644,6 +688,12 @@ function githubStrategy(settings) {
644
688
  },
645
689
  };
646
690
  }
691
+ function isGitHubRepositoryCreateConflict(error) {
692
+ return (isIntegrationError(error, INTEGRATION_ERROR_CODE.conflict) &&
693
+ (error.data.status === 409 ||
694
+ (error.data.status === 422 &&
695
+ hasRepositoryNameConflictDetail(error.data.detail))));
696
+ }
647
697
  function missingGitHubNodeId(transport, resource) {
648
698
  return new IntegrationError({
649
699
  code: INTEGRATION_ERROR_CODE.failed,
@@ -1 +1 @@
1
- {"version":3,"file":"gitlab.d.ts","sourceRoot":"","sources":["../../src/code-hosts/gitlab.ts"],"names":[],"mappings":"AA4CA,OAAO,EACL,KAAK,eAAe,EAMrB,MAAM,aAAa,CAAC;AA4BrB,eAAO,MAAM,qBAAqB,4DAA0B,CAAC;AA8B7D,eAAO,MAAM,qBAAqB,sCAAuC,CAAC;AAC1E,eAAO,MAAM,4BAA4B;;;CAGD,CAAC;AAEzC,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AA2BF,wBAAgB,MAAM,kJAkErB"}
1
+ {"version":3,"file":"gitlab.d.ts","sourceRoot":"","sources":["../../src/code-hosts/gitlab.ts"],"names":[],"mappings":"AAgDA,OAAO,EACL,KAAK,eAAe,EAMrB,MAAM,aAAa,CAAC;AAgCrB,eAAO,MAAM,qBAAqB,4DAA0B,CAAC;AA8B7D,eAAO,MAAM,qBAAqB,sCAAuC,CAAC;AAC1E,eAAO,MAAM,4BAA4B;;;CAGD,CAAC;AAEzC,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AA0EF,wBAAgB,MAAM,kJAiErB"}