repo-sdk 0.1.1 → 0.1.3

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 CHANGED
@@ -74,7 +74,7 @@ npm install
74
74
  | `npm run test:live` | Gated live provider tests (env vars documented in CONTRIBUTING) |
75
75
  | `npm run typecheck` | Type-check without emitting |
76
76
  | `npm run lint` | Lint the codebase |
77
- | `npm run format` | Format with prettier |
77
+ | `npm run fmt` | Format with prettier |
78
78
 
79
79
  Docs site:
80
80
 
@@ -1,4 +1,4 @@
1
- import { O as ParsedWebhookEvent, P as RepoProvider, v as IncomingWebhookRequest, z as TokenProvider } from "./types-CTeiAxMy.js";
1
+ import { B as TokenProvider, F as RepoProvider, O as ParsedWebhookEvent, v as IncomingWebhookRequest } from "./types-DfiRluvJ.js";
2
2
  import { t as VerifyWebhookParams$1 } from "./verify-p6JsExms.js";
3
3
  //#region src/providers/azure-devops/auth.d.ts
4
4
  /**
@@ -1,6 +1,6 @@
1
1
  import { t as RepoError } from "./errors-Cxq_CqYv.js";
2
2
  import { n as decodeCursor, r as encodeCursor } from "./pagination-CWWHSu8I.js";
3
- import { a as isRecord, i as filenameFromContentDisposition, n as commitWebUrlBuilder, s as HttpClient } from "./shared-CuiOaaLD.js";
3
+ import { a as filenameFromContentDisposition, o as isRecord, r as commitWebUrlBuilder, t as HttpClient } from "./http-C3hAQeY0.js";
4
4
  import { a as headShaOrUndefined, n as timingSafeEqual, r as toIncomingWebhook } from "./verify-BBMVxz8q.js";
5
5
  //#region src/providers/azure-devops/auth.ts
6
6
  /**
@@ -1,4 +1,4 @@
1
- import { O as ParsedWebhookEvent, P as RepoProvider, v as IncomingWebhookRequest, z as TokenProvider } from "./types-CTeiAxMy.js";
1
+ import { B as TokenProvider, F as RepoProvider, O as ParsedWebhookEvent, v as IncomingWebhookRequest } from "./types-DfiRluvJ.js";
2
2
  import { t as VerifyWebhookParams } from "./verify-p6JsExms.js";
3
3
  //#region src/providers/bitbucket/index.d.ts
4
4
  interface BitbucketProviderOptions {
package/dist/bitbucket.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as RepoError } from "./errors-Cxq_CqYv.js";
2
2
  import { n as decodeCursor, r as encodeCursor, t as assertSameOriginUrl } from "./pagination-CWWHSu8I.js";
3
- import { a as isRecord, i as filenameFromContentDisposition, n as commitWebUrlBuilder, s as HttpClient, t as clampPerPage } from "./shared-CuiOaaLD.js";
3
+ import { a as filenameFromContentDisposition, n as clampPerPage, o as isRecord, r as commitWebUrlBuilder, t as HttpClient } from "./http-C3hAQeY0.js";
4
4
  import { a as headShaOrUndefined, i as verifyHmacSignature, r as toIncomingWebhook } from "./verify-BBMVxz8q.js";
5
5
  //#region src/providers/bitbucket/index.ts
6
6
  const BASE_URL = "https://api.bitbucket.org/2.0";
@@ -1,4 +1,4 @@
1
- import { P as RepoProvider, z as TokenProvider } from "./types-CTeiAxMy.js";
1
+ import { B as TokenProvider, F as RepoProvider } from "./types-DfiRluvJ.js";
2
2
  //#region src/providers/git-http/index.d.ts
3
3
  interface GitHttpProviderOptions {
4
4
  /**
package/dist/git-http.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as RepoError } from "./errors-Cxq_CqYv.js";
2
2
  import { a as stringToBase64, n as decodeCursor, r as encodeCursor } from "./pagination-CWWHSu8I.js";
3
- import { s as HttpClient, t as clampPerPage } from "./shared-CuiOaaLD.js";
3
+ import { n as clampPerPage, t as HttpClient } from "./http-C3hAQeY0.js";
4
4
  //#region src/providers/git-http/index.ts
5
5
  const CAPABILITIES = {
6
6
  userProfile: false,
package/dist/gitea.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { O as ParsedWebhookEvent, P as RepoProvider, v as IncomingWebhookRequest, z as TokenProvider } from "./types-CTeiAxMy.js";
1
+ import { B as TokenProvider, F as RepoProvider, O as ParsedWebhookEvent, v as IncomingWebhookRequest } from "./types-DfiRluvJ.js";
2
2
  import { t as VerifyWebhookParams } from "./verify-p6JsExms.js";
3
3
  //#region src/providers/gitea/index.d.ts
4
4
  interface GiteaProviderOptions {
package/dist/gitea.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as RepoError } from "./errors-Cxq_CqYv.js";
2
2
  import { n as decodeCursor, r as encodeCursor, t as assertSameOriginUrl } from "./pagination-CWWHSu8I.js";
3
- import { a as isRecord, i as filenameFromContentDisposition, n as commitWebUrlBuilder, o as parseLinkNext, r as encodeRefPath, s as HttpClient, t as clampPerPage } from "./shared-CuiOaaLD.js";
3
+ import { a as filenameFromContentDisposition, i as encodeRefPath, n as clampPerPage, o as isRecord, r as commitWebUrlBuilder, s as parseLinkNext, t as HttpClient } from "./http-C3hAQeY0.js";
4
4
  import { a as headShaOrUndefined, n as timingSafeEqual, r as toIncomingWebhook, t as hmacSha256Hex } from "./verify-BBMVxz8q.js";
5
5
  //#region src/providers/gitea/index.ts
6
6
  const DEFAULT_BASE_URL = "https://gitea.com/api/v1";
package/dist/github.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { O as ParsedWebhookEvent, P as RepoProvider, v as IncomingWebhookRequest, z as TokenProvider } from "./types-CTeiAxMy.js";
1
+ import { B as TokenProvider, D as Page, F as RepoProvider, O as ParsedWebhookEvent, v as IncomingWebhookRequest } from "./types-DfiRluvJ.js";
2
2
  import { t as VerifyWebhookParams } from "./verify-p6JsExms.js";
3
3
  //#region src/providers/github/app-auth.d.ts
4
4
  interface InstallationToken {
@@ -43,8 +43,42 @@ interface GitHubRepoProvider extends RepoProvider {
43
43
  declare const commitWebUrl: (repoWebUrl: string, sha: string) => string;
44
44
  declare function github(options: GitHubProviderOptions): GitHubRepoProvider;
45
45
  //#endregion
46
+ //#region src/providers/github/installations.d.ts
47
+ interface ListUserInstallationsParams {
48
+ /**
49
+ * A user access token — e.g. the OAuth token minted by the GitHub App web
50
+ * login flow. Installation tokens cannot call this endpoint.
51
+ */
52
+ token: string;
53
+ /** GHES API root (`/api/v3`); defaults to `https://api.github.com`. */
54
+ baseUrl?: string;
55
+ fetch?: typeof fetch;
56
+ limit?: number;
57
+ cursor?: string;
58
+ signal?: AbortSignal;
59
+ }
60
+ interface GitHubInstallationAccount {
61
+ id: string;
62
+ login: string;
63
+ kind: 'user' | 'organization';
64
+ avatarUrl?: string;
65
+ }
66
+ interface GitHubUserInstallation {
67
+ id: string;
68
+ /** Account the app is installed on; absent for enterprise-level installations. */
69
+ account?: GitHubInstallationAccount;
70
+ raw: unknown;
71
+ }
72
+ /**
73
+ * Lists the GitHub App installations the given user can access
74
+ * (`GET /user/installations`). Standalone by design: the endpoint requires a
75
+ * user access token, a different credential from the installation token the
76
+ * `github` provider authenticates its requests with.
77
+ */
78
+ declare function listUserInstallations(params: ListUserInstallationsParams): Promise<Page<GitHubUserInstallation>>;
79
+ //#endregion
46
80
  //#region src/providers/github/webhooks.d.ts
47
81
  declare function verifyWebhook(params: VerifyWebhookParams): Promise<boolean>;
48
82
  declare function parseWebhookEvent(input: Request | IncomingWebhookRequest): Promise<ParsedWebhookEvent>;
49
83
  //#endregion
50
- export { type GitHubAppAuth, type GitHubAuth, type GitHubInstallationToken, type GitHubProviderOptions, type GitHubRepoProvider, type GitHubTokenAuth, type VerifyWebhookParams, commitWebUrl, github, parseWebhookEvent, verifyWebhook };
84
+ export { type GitHubAppAuth, type GitHubAuth, type GitHubInstallationAccount, type GitHubInstallationToken, type GitHubProviderOptions, type GitHubRepoProvider, type GitHubTokenAuth, type GitHubUserInstallation, type ListUserInstallationsParams, type VerifyWebhookParams, commitWebUrl, github, listUserInstallations, parseWebhookEvent, verifyWebhook };
package/dist/github.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { n as codeFromStatus, t as RepoError } from "./errors-Cxq_CqYv.js";
2
2
  import { i as bytesToBase64Url, n as decodeCursor, r as encodeCursor, t as assertSameOriginUrl } from "./pagination-CWWHSu8I.js";
3
- import { a as isRecord, i as filenameFromContentDisposition, n as commitWebUrlBuilder, o as parseLinkNext, r as encodeRefPath, s as HttpClient, t as clampPerPage } from "./shared-CuiOaaLD.js";
3
+ import { a as filenameFromContentDisposition, i as encodeRefPath, n as clampPerPage, o as isRecord, r as commitWebUrlBuilder, s as parseLinkNext, t as HttpClient } from "./http-C3hAQeY0.js";
4
4
  import { a as headShaOrUndefined, i as verifyHmacSignature, r as toIncomingWebhook } from "./verify-BBMVxz8q.js";
5
5
  //#region src/providers/github/app-auth.ts
6
6
  const RSA_ALGORITHM_IDENTIFIER = [
@@ -205,9 +205,10 @@ var AppTokenSource = class {
205
205
  }
206
206
  async appRequest(path, method, jwt) {
207
207
  const url = joinUrl(this.baseUrl, path);
208
+ const { fetchImpl } = this;
208
209
  let response;
209
210
  try {
210
- response = await this.fetchImpl(url, {
211
+ response = await fetchImpl(url, {
211
212
  method,
212
213
  headers: {
213
214
  Authorization: `Bearer ${jwt}`,
@@ -242,11 +243,23 @@ var AppTokenSource = class {
242
243
  });
243
244
  }
244
245
  };
245
- //#endregion
246
- //#region src/providers/github/index.ts
247
- const DEFAULT_BASE_URL = "https://api.github.com";
248
246
  const API_VERSION = "2022-11-28";
249
247
  const USER_AGENT = "repo-sdk";
248
+ function mapError(status, body, response) {
249
+ const message = isRecord(body) && typeof body.message === "string" ? body.message : void 0;
250
+ let code;
251
+ if (status === 429 || status === 403 && response.headers.get("x-ratelimit-remaining") === "0") code = "rate_limited";
252
+ return {
253
+ code,
254
+ message
255
+ };
256
+ }
257
+ function nextCursor(response) {
258
+ const next = parseLinkNext(response.headers.get("link"));
259
+ return next === void 0 ? void 0 : encodeCursor("github", { url: next });
260
+ }
261
+ //#endregion
262
+ //#region src/providers/github/index.ts
250
263
  const CAPABILITIES = {
251
264
  tagDates: false,
252
265
  repoSearch: true,
@@ -297,10 +310,6 @@ function createTokenSource(auth, baseUrl, fetchImpl) {
297
310
  userAgent: USER_AGENT
298
311
  });
299
312
  }
300
- function nextCursor(response) {
301
- const next = parseLinkNext(response.headers.get("link"));
302
- return next === void 0 ? void 0 : encodeCursor("github", { url: next });
303
- }
304
313
  function gitHostFromBaseUrl(baseUrl) {
305
314
  const url = new URL(baseUrl);
306
315
  return url.hostname === "api.github.com" ? "github.com" : url.host;
@@ -410,22 +419,13 @@ function toWebhook(hook) {
410
419
  raw: hook
411
420
  };
412
421
  }
413
- function mapError(status, body, response) {
414
- const message = isRecord(body) && typeof body.message === "string" ? body.message : void 0;
415
- let code;
416
- if (status === 429 || status === 403 && response.headers.get("x-ratelimit-remaining") === "0") code = "rate_limited";
417
- return {
418
- code,
419
- message
420
- };
421
- }
422
422
  /**
423
423
  * Builds the human-facing web URL for a commit from the repository's web URL
424
424
  * (`Repository.urls.web`) and a commit SHA — no API request needed.
425
425
  */
426
426
  const commitWebUrl = commitWebUrlBuilder("commit");
427
427
  function github(options) {
428
- const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;
428
+ const baseUrl = options.baseUrl ?? "https://api.github.com";
429
429
  const fetchImpl = options.fetch ?? fetch;
430
430
  const tokenSource = createTokenSource(options.auth, baseUrl, fetchImpl);
431
431
  const webBase = `https://${gitHostFromBaseUrl(baseUrl)}`;
@@ -863,6 +863,58 @@ function github(options) {
863
863
  };
864
864
  }
865
865
  //#endregion
866
+ //#region src/providers/github/installations.ts
867
+ function toInstallation(payload) {
868
+ const account = payload.account;
869
+ return {
870
+ id: String(payload.id),
871
+ account: account?.login === void 0 ? void 0 : {
872
+ id: String(account.id),
873
+ login: account.login,
874
+ kind: account.type === "Organization" ? "organization" : "user",
875
+ avatarUrl: account.avatar_url
876
+ },
877
+ raw: payload
878
+ };
879
+ }
880
+ /**
881
+ * Lists the GitHub App installations the given user can access
882
+ * (`GET /user/installations`). Standalone by design: the endpoint requires a
883
+ * user access token, a different credential from the installation token the
884
+ * `github` provider authenticates its requests with.
885
+ */
886
+ async function listUserInstallations(params) {
887
+ const { token } = params;
888
+ const baseUrl = params.baseUrl ?? "https://api.github.com";
889
+ const http = new HttpClient({
890
+ provider: "github",
891
+ baseUrl,
892
+ fetchImpl: params.fetch,
893
+ authHeaders: () => ({
894
+ Authorization: `Bearer ${token}`,
895
+ Accept: "application/vnd.github+json",
896
+ "X-GitHub-Api-Version": API_VERSION,
897
+ "User-Agent": USER_AGENT
898
+ }),
899
+ mapError,
900
+ secrets: () => [token]
901
+ });
902
+ let path = "/user/installations";
903
+ let query;
904
+ if (params.cursor) {
905
+ const { url } = decodeCursor("github", params.cursor);
906
+ path = assertSameOriginUrl("github", baseUrl, url);
907
+ } else query = { per_page: clampPerPage(params.limit) };
908
+ const { data, response } = await http.json(path, {
909
+ query,
910
+ signal: params.signal
911
+ });
912
+ return {
913
+ data: (data.installations ?? []).map(toInstallation),
914
+ cursor: nextCursor(response)
915
+ };
916
+ }
917
+ //#endregion
866
918
  //#region src/providers/github/webhooks.ts
867
919
  function verifyWebhook(params) {
868
920
  return verifyHmacSignature(params, { header: "x-hub-signature-256" });
@@ -897,4 +949,4 @@ async function parseWebhookEvent(input) {
897
949
  };
898
950
  }
899
951
  //#endregion
900
- export { commitWebUrl, github, parseWebhookEvent, verifyWebhook };
952
+ export { commitWebUrl, github, listUserInstallations, parseWebhookEvent, verifyWebhook };
package/dist/gitlab.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { O as ParsedWebhookEvent, P as RepoProvider, v as IncomingWebhookRequest, z as TokenProvider } from "./types-CTeiAxMy.js";
1
+ import { B as TokenProvider, F as RepoProvider, O as ParsedWebhookEvent, v as IncomingWebhookRequest } from "./types-DfiRluvJ.js";
2
2
  import { t as VerifyWebhookParams } from "./verify-p6JsExms.js";
3
3
  //#region src/providers/gitlab/index.d.ts
4
4
  interface GitLabProviderOptions {
package/dist/gitlab.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as RepoError } from "./errors-Cxq_CqYv.js";
2
2
  import { n as decodeCursor, r as encodeCursor, t as assertSameOriginUrl } from "./pagination-CWWHSu8I.js";
3
- import { a as isRecord, i as filenameFromContentDisposition, n as commitWebUrlBuilder, o as parseLinkNext, s as HttpClient, t as clampPerPage } from "./shared-CuiOaaLD.js";
3
+ import { a as filenameFromContentDisposition, n as clampPerPage, o as isRecord, r as commitWebUrlBuilder, s as parseLinkNext, t as HttpClient } from "./http-C3hAQeY0.js";
4
4
  import { a as headShaOrUndefined, n as timingSafeEqual, r as toIncomingWebhook } from "./verify-BBMVxz8q.js";
5
5
  //#region src/providers/gitlab/index.ts
6
6
  const DEFAULT_BASE_URL = "https://gitlab.com/api/v4";
@@ -1,5 +1,37 @@
1
1
  import { n as codeFromStatus, r as parseRetryAfter, t as RepoError } from "./errors-Cxq_CqYv.js";
2
+ //#region src/providers/shared.ts
3
+ function isRecord(value) {
4
+ return typeof value === "object" && value !== null;
5
+ }
6
+ function clampPerPage(limit, max = 100) {
7
+ if (limit === void 0) return void 0;
8
+ return Math.min(Math.max(1, Math.trunc(limit)), max);
9
+ }
10
+ function parseLinkNext(header) {
11
+ if (!header) return void 0;
12
+ for (const part of header.split(",")) {
13
+ const match = part.match(/<([^>]+)>\s*;\s*rel="([^"]+)"/);
14
+ if (match && match[2] === "next") return match[1];
15
+ }
16
+ }
17
+ /** Percent-encodes a ref path while keeping its `/` separators intact. */
18
+ function encodeRefPath(ref) {
19
+ return ref.split("/").map(encodeURIComponent).join("/");
20
+ }
21
+ function commitWebUrlBuilder(pathSegment) {
22
+ return function commitWebUrl(repoWebUrl, sha) {
23
+ return `${repoWebUrl.replace(/\/+$/, "")}/${pathSegment}/${sha}`;
24
+ };
25
+ }
26
+ function filenameFromContentDisposition(header) {
27
+ if (!header) return void 0;
28
+ const extended = header.match(/filename\*=(?:UTF-8'')?([^;]+)/i);
29
+ if (extended?.[1]) return decodeURIComponent(extended[1].trim().replace(/^"|"$/g, ""));
30
+ return header.match(/filename="?([^";]+)"?/i)?.[1]?.trim();
31
+ }
32
+ //#endregion
2
33
  //#region src/http.ts
34
+ const USER_AGENT = "repo-sdk";
3
35
  var HttpClient = class {
4
36
  options;
5
37
  constructor(options) {
@@ -23,6 +55,7 @@ var HttpClient = class {
23
55
  ...await this.options.authHeaders({ forceRefresh }),
24
56
  ...options.headers
25
57
  };
58
+ headers["User-Agent"] ??= USER_AGENT;
26
59
  if (body !== void 0) headers["Content-Type"] ??= "application/json";
27
60
  try {
28
61
  return await fetchImpl(url, {
@@ -71,35 +104,4 @@ var HttpClient = class {
71
104
  }
72
105
  };
73
106
  //#endregion
74
- //#region src/providers/shared.ts
75
- function isRecord(value) {
76
- return typeof value === "object" && value !== null;
77
- }
78
- function clampPerPage(limit, max = 100) {
79
- if (limit === void 0) return void 0;
80
- return Math.min(Math.max(1, Math.trunc(limit)), max);
81
- }
82
- function parseLinkNext(header) {
83
- if (!header) return void 0;
84
- for (const part of header.split(",")) {
85
- const match = part.match(/<([^>]+)>\s*;\s*rel="([^"]+)"/);
86
- if (match && match[2] === "next") return match[1];
87
- }
88
- }
89
- /** Percent-encodes a ref path while keeping its `/` separators intact. */
90
- function encodeRefPath(ref) {
91
- return ref.split("/").map(encodeURIComponent).join("/");
92
- }
93
- function commitWebUrlBuilder(pathSegment) {
94
- return function commitWebUrl(repoWebUrl, sha) {
95
- return `${repoWebUrl.replace(/\/+$/, "")}/${pathSegment}/${sha}`;
96
- };
97
- }
98
- function filenameFromContentDisposition(header) {
99
- if (!header) return void 0;
100
- const extended = header.match(/filename\*=(?:UTF-8'')?([^;]+)/i);
101
- if (extended?.[1]) return decodeURIComponent(extended[1].trim().replace(/^"|"$/g, ""));
102
- return header.match(/filename="?([^";]+)"?/i)?.[1]?.trim();
103
- }
104
- //#endregion
105
- export { isRecord as a, filenameFromContentDisposition as i, commitWebUrlBuilder as n, parseLinkNext as o, encodeRefPath as r, HttpClient as s, clampPerPage as t };
107
+ export { filenameFromContentDisposition as a, encodeRefPath as i, clampPerPage as n, isRecord as o, commitWebUrlBuilder as r, parseLinkNext as s, HttpClient as t };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as ProviderSearchRefsParams, B as UpdateWebhookParams, C as ListTagsParams, D as Page, E as NamespaceKind, F as Repository, H as Webhook, I as ResolveRefParams, L as SearchRefsParams, M as RefType, N as RepoCapabilities, O as ParsedWebhookEvent, P as RepoProvider, R as Tag, S as ListRepositoriesParams, T as Namespace, U as WebhookEventType, V as UserRef, W as WebhookVerificationMethod, _ as GitActor, a as CloneUrl, b as ListCommitsParams, c as DeleteWebhookParams, d as GetBranchParams, f as GetCloneUrlParams, g as GetWebhookParams, h as GetTagParams, i as Branch, j as RefMatch, k as ProviderName, l as DownloadArchiveParams, m as GetRepositoryParams, n as ArchiveFormat, o as Commit, p as GetCommitParams, r as AuthenticatedUser, s as CreateWebhookParams, t as Archive, u as GetAuthenticatedUserParams, v as IncomingWebhookRequest, w as ListWebhooksParams, x as ListNamespacesParams, y as ListBranchesParams, z as TokenProvider } from "./types-CTeiAxMy.js";
1
+ import { A as ProviderRefMatch, B as TokenProvider, C as ListTagsParams, D as Page, E as NamespaceKind, F as RepoProvider, G as WebhookVerificationMethod, H as UserRef, I as Repository, L as ResolveRefParams, M as RefMatch, N as RefType, O as ParsedWebhookEvent, P as RepoCapabilities, R as SearchRefsParams, S as ListRepositoriesParams, T as Namespace, U as Webhook, V as UpdateWebhookParams, W as WebhookEventType, _ as GitActor, a as CloneUrl, b as ListCommitsParams, c as DeleteWebhookParams, d as GetBranchParams, f as GetCloneUrlParams, g as GetWebhookParams, h as GetTagParams, i as Branch, j as ProviderSearchRefsParams, k as ProviderName, l as DownloadArchiveParams, m as GetRepositoryParams, n as ArchiveFormat, o as Commit, p as GetCommitParams, r as AuthenticatedUser, s as CreateWebhookParams, t as Archive, u as GetAuthenticatedUserParams, v as IncomingWebhookRequest, w as ListWebhooksParams, x as ListNamespacesParams, y as ListBranchesParams, z as Tag } from "./types-DfiRluvJ.js";
2
2
  //#region src/client.d.ts
3
3
  interface RetryOptions {
4
4
  rateLimit?: boolean;
@@ -92,4 +92,4 @@ declare class RepoError extends Error {
92
92
  constructor(message: string, options: RepoErrorOptions);
93
93
  }
94
94
  //#endregion
95
- export { type Archive, type ArchiveFormat, type AuthenticatedUser, type Branch, type CloneUrl, type Commit, type CreateClientOptions, type CreateWebhookParams, type DeleteWebhookParams, type DetectWebhookProviderInput, type DownloadArchiveParams, type GetAuthenticatedUserParams, type GetBranchParams, type GetCloneUrlParams, type GetCommitParams, type GetRepositoryParams, type GetTagParams, type GetWebhookParams, type GitActor, type IncomingWebhookRequest, type ListBranchesParams, type ListCommitsParams, type ListNamespacesParams, type ListRepositoriesParams, type ListTagsParams, type ListWebhooksParams, type Namespace, type NamespaceKind, type Page, type ParsedWebhookEvent, type ProviderName, type ProviderSearchRefsParams, type RefMatch, type RefType, type RepoCapabilities, type RepoClient, RepoError, type RepoErrorCode, type RepoErrorOptions, type RepoProvider, type Repository, type ResolveRefParams, type RetryOptions, type SearchRefsParams, type Tag, type TokenProvider, type UpdateWebhookParams, type UserRef, type Webhook, type WebhookEventType, type WebhookVerificationMethod, createClient, detectWebhookProvider };
95
+ export { type Archive, type ArchiveFormat, type AuthenticatedUser, type Branch, type CloneUrl, type Commit, type CreateClientOptions, type CreateWebhookParams, type DeleteWebhookParams, type DetectWebhookProviderInput, type DownloadArchiveParams, type GetAuthenticatedUserParams, type GetBranchParams, type GetCloneUrlParams, type GetCommitParams, type GetRepositoryParams, type GetTagParams, type GetWebhookParams, type GitActor, type IncomingWebhookRequest, type ListBranchesParams, type ListCommitsParams, type ListNamespacesParams, type ListRepositoriesParams, type ListTagsParams, type ListWebhooksParams, type Namespace, type NamespaceKind, type Page, type ParsedWebhookEvent, type ProviderName, type ProviderRefMatch, type ProviderSearchRefsParams, type RefMatch, type RefType, type RepoCapabilities, type RepoClient, RepoError, type RepoErrorCode, type RepoErrorOptions, type RepoProvider, type Repository, type ResolveRefParams, type RetryOptions, type SearchRefsParams, type Tag, type TokenProvider, type UpdateWebhookParams, type UserRef, type Webhook, type WebhookEventType, type WebhookVerificationMethod, createClient, detectWebhookProvider };
package/dist/index.js CHANGED
@@ -10,29 +10,39 @@ const ALL_REF_TYPES = [
10
10
  "commit"
11
11
  ];
12
12
  const COMMIT_SHA_PATTERN = /^[0-9a-f]{4,40}$/i;
13
- function branchMatch(branch) {
13
+ const FULL_COMMIT_SHA_PATTERN = /^[0-9a-f]{40}$/i;
14
+ const BRANCH_REF_PREFIX = "refs/heads/";
15
+ const TAG_REF_PREFIX = "refs/tags/";
16
+ function qualifyRef(match) {
17
+ const ref = match.type === "branch" ? `${BRANCH_REF_PREFIX}${match.name}` : match.type === "tag" ? `${TAG_REF_PREFIX}${match.name}` : match.sha;
14
18
  return {
19
+ ...match,
20
+ ref
21
+ };
22
+ }
23
+ function branchMatch(branch) {
24
+ return qualifyRef({
15
25
  type: "branch",
16
26
  name: branch.name,
17
27
  sha: branch.sha,
18
28
  raw: branch.raw
19
- };
29
+ });
20
30
  }
21
31
  function tagMatch(tag) {
22
- return {
32
+ return qualifyRef({
23
33
  type: "tag",
24
34
  name: tag.name,
25
35
  sha: tag.sha,
26
36
  raw: tag.raw
27
- };
37
+ });
28
38
  }
29
39
  function commitMatch(commit) {
30
- return {
40
+ return qualifyRef({
31
41
  type: "commit",
32
42
  name: commit.sha,
33
43
  sha: commit.sha,
34
44
  raw: commit.raw
35
- };
45
+ });
36
46
  }
37
47
  function createClient(options) {
38
48
  const { provider } = options;
@@ -192,7 +202,7 @@ function createClient(options) {
192
202
  types: refTypes,
193
203
  limit,
194
204
  signal
195
- })) : [], types.includes("commit") && COMMIT_SHA_PATTERN.test(query) ? withRetry(() => provider.getCommit({
205
+ })).then((matches) => matches.map(qualifyRef)) : [], types.includes("commit") && COMMIT_SHA_PATTERN.test(query) ? withRetry(() => provider.getCommit({
196
206
  repo,
197
207
  ref: query,
198
208
  signal
@@ -205,8 +215,16 @@ function createClient(options) {
205
215
  resolve: async (params) => {
206
216
  requireNonEmpty(params.repo, "repo");
207
217
  requireNonEmpty(params.ref, "ref");
208
- const { repo, ref, type, signal } = params;
209
- if (ref === "HEAD" && (type === void 0 || type === "branch")) {
218
+ const { repo, signal } = params;
219
+ let { ref, type } = params;
220
+ const impliedType = ref.startsWith(BRANCH_REF_PREFIX) ? "branch" : ref.startsWith(TAG_REF_PREFIX) ? "tag" : void 0;
221
+ if (impliedType !== void 0) {
222
+ if (type !== void 0 && type !== impliedType) fail("validation", `Fully-qualified ref '${ref}' contradicts type '${type}'`);
223
+ type = impliedType;
224
+ ref = ref.slice(impliedType === "branch" ? 11 : 10);
225
+ requireNonEmpty(ref, "ref");
226
+ }
227
+ if (impliedType === void 0 && ref === "HEAD" && (type === void 0 || type === "branch")) {
210
228
  const defaultBranch = (await withRetry(() => provider.getRepository({
211
229
  repo,
212
230
  signal
@@ -226,6 +244,17 @@ function createClient(options) {
226
244
  ref,
227
245
  signal
228
246
  })));
247
+ let commitProbeError;
248
+ if (type === void 0 && FULL_COMMIT_SHA_PATTERN.test(ref)) try {
249
+ return commitMatch(await withRetry(() => provider.getCommit({
250
+ repo,
251
+ ref,
252
+ signal
253
+ })));
254
+ } catch (error) {
255
+ if (error instanceof RepoError && (error.code === "not_found" || error.code === "validation" || error.code === "unsupported")) commitProbeError = error;
256
+ else throw error;
257
+ }
229
258
  const [tagResult, branchResult] = await Promise.allSettled([type === "branch" ? void 0 : withRetry(() => provider.getTag({
230
259
  repo,
231
260
  name: ref,
@@ -242,11 +271,14 @@ function createClient(options) {
242
271
  if (branchResult.status === "fulfilled") {
243
272
  if (branchResult.value !== void 0) return branchMatch(branchResult.value);
244
273
  } else if (!isMiss(branchResult)) throw branchResult.reason;
245
- if (type === void 0 && COMMIT_SHA_PATTERN.test(ref)) return commitMatch(await withRetry(() => provider.getCommit({
246
- repo,
247
- ref,
248
- signal
249
- })));
274
+ if (type === void 0 && COMMIT_SHA_PATTERN.test(ref)) {
275
+ if (commitProbeError !== void 0) throw commitProbeError;
276
+ return commitMatch(await withRetry(() => provider.getCommit({
277
+ repo,
278
+ ref,
279
+ signal
280
+ })));
281
+ }
250
282
  throw new RepoError(`Ref not found: ${ref}`, {
251
283
  code: "not_found",
252
284
  provider: provider.name
package/dist/testing.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { E as NamespaceKind, F as Repository, H as Webhook, N as RepoCapabilities, P as RepoProvider, R as Tag, T as Namespace, U as WebhookEventType, i as Branch, k as ProviderName, o as Commit } from "./types-CTeiAxMy.js";
1
+ import { E as NamespaceKind, F as RepoProvider, I as Repository, P as RepoCapabilities, T as Namespace, U as Webhook, W as WebhookEventType, i as Branch, k as ProviderName, o as Commit, z as Tag } from "./types-DfiRluvJ.js";
2
2
  //#region src/providers/testing/index.d.ts
3
3
  interface InMemoryNamespaceSeed {
4
4
  id: string;
@@ -93,10 +93,14 @@ type RefType = 'branch' | 'tag' | 'commit';
93
93
  interface RefMatch {
94
94
  type: RefType;
95
95
  name: string;
96
+ /** Fully-qualified form — `refs/heads/<name>` for branches, `refs/tags/<name>` for tags, the SHA itself for commits. Any `ref` value round-trips through `refs.resolve`. */
97
+ ref: string;
96
98
  /** SHA the ref points to. In `refs.search` results on GitHub and Gitea, annotated tags carry the tag object SHA rather than the commit; `refs.resolve` and `tags.get` always return the peeled commit SHA. */
97
99
  sha: string;
98
100
  raw: unknown;
99
101
  }
102
+ /** `RefMatch` as produced by provider adapters, before the client derives the fully-qualified `ref`. */
103
+ type ProviderRefMatch = Omit<RefMatch, 'ref'>;
100
104
  interface Webhook {
101
105
  id: string;
102
106
  url: string;
@@ -188,8 +192,9 @@ interface GetTagParams extends BaseParams {
188
192
  }
189
193
  interface ResolveRefParams extends BaseParams {
190
194
  repo: string;
195
+ /** A branch or tag name, a fully-qualified ref (`refs/heads/…`, `refs/tags/…`), a 4–40 hex commit SHA, or `HEAD`. */
191
196
  ref: string;
192
- /** Restrict resolution to one ref type; when omitted, branches and tags are tried (tag wins on collision) before falling back to commit SHAs. */
197
+ /** Restrict resolution to one ref type; when omitted, a full 40-hex `ref` is tried as a commit first, then branches and tags (tag wins on collision), then abbreviated commit SHAs. A fully-qualified `ref` implies its type; passing a contradicting `type` throws a validation error. */
193
198
  type?: RefType;
194
199
  }
195
200
  interface SearchRefsParams extends BaseParams {
@@ -269,7 +274,7 @@ interface RepoProvider {
269
274
  /** Exact-match a tag by name, peeled to the commit SHA for annotated tags; throws `not_found` on a miss. */
270
275
  getTag(params: GetTagParams): Promise<Tag>;
271
276
  /** Prefix-match refs of the requested types, branches before tags, at most `limit` results. */
272
- searchRefs(params: ProviderSearchRefsParams): Promise<RefMatch[]>;
277
+ searchRefs(params: ProviderSearchRefsParams): Promise<ProviderRefMatch[]>;
273
278
  downloadArchive(params: DownloadArchiveParams): Promise<Archive>;
274
279
  getCloneUrl(params: GetCloneUrlParams): Promise<CloneUrl>;
275
280
  createWebhook(params: CreateWebhookParams): Promise<Webhook>;
@@ -279,4 +284,4 @@ interface RepoProvider {
279
284
  deleteWebhook(params: DeleteWebhookParams): Promise<void>;
280
285
  }
281
286
  //#endregion
282
- export { ProviderSearchRefsParams as A, UpdateWebhookParams as B, ListTagsParams as C, Page as D, NamespaceKind as E, Repository as F, Webhook as H, ResolveRefParams as I, SearchRefsParams as L, RefType as M, RepoCapabilities as N, ParsedWebhookEvent as O, RepoProvider as P, Tag as R, ListRepositoriesParams as S, Namespace as T, WebhookEventType as U, UserRef as V, WebhookVerificationMethod as W, GitActor as _, CloneUrl as a, ListCommitsParams as b, DeleteWebhookParams as c, GetBranchParams as d, GetCloneUrlParams as f, GetWebhookParams as g, GetTagParams as h, Branch as i, RefMatch as j, ProviderName as k, DownloadArchiveParams as l, GetRepositoryParams as m, ArchiveFormat as n, Commit as o, GetCommitParams as p, AuthenticatedUser as r, CreateWebhookParams as s, Archive as t, GetAuthenticatedUserParams as u, IncomingWebhookRequest as v, ListWebhooksParams as w, ListNamespacesParams as x, ListBranchesParams as y, TokenProvider as z };
287
+ export { ProviderRefMatch as A, TokenProvider as B, ListTagsParams as C, Page as D, NamespaceKind as E, RepoProvider as F, WebhookVerificationMethod as G, UserRef as H, Repository as I, ResolveRefParams as L, RefMatch as M, RefType as N, ParsedWebhookEvent as O, RepoCapabilities as P, SearchRefsParams as R, ListRepositoriesParams as S, Namespace as T, Webhook as U, UpdateWebhookParams as V, WebhookEventType as W, GitActor as _, CloneUrl as a, ListCommitsParams as b, DeleteWebhookParams as c, GetBranchParams as d, GetCloneUrlParams as f, GetWebhookParams as g, GetTagParams as h, Branch as i, ProviderSearchRefsParams as j, ProviderName as k, DownloadArchiveParams as l, GetRepositoryParams as m, ArchiveFormat as n, Commit as o, GetCommitParams as p, AuthenticatedUser as r, CreateWebhookParams as s, Archive as t, GetAuthenticatedUserParams as u, IncomingWebhookRequest as v, ListWebhooksParams as w, ListNamespacesParams as x, ListBranchesParams as y, Tag as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repo-sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Unified TypeScript SDK for git providers — GitHub, GitLab, Bitbucket, Azure DevOps, Gitea",
5
5
  "license": "MIT",
6
6
  "author": "Robin Genz <mail@robingenz.dev>",
@@ -76,8 +76,8 @@
76
76
  "test:watch": "vitest",
77
77
  "typecheck": "tsc --noEmit",
78
78
  "lint": "eslint .",
79
- "format": "prettier --write .",
80
- "format:check": "prettier --check .",
79
+ "fmt": "prettier --write .",
80
+ "fmt:check": "prettier --check .",
81
81
  "docs:dev": "blume dev",
82
82
  "docs:build": "blume build && rm -rf .blume-dist && cp -R dist .blume-dist",
83
83
  "docs:preview": "blume preview",