repo-sdk 0.1.2 → 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,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
  /**
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";
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.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 { B as TokenProvider, F as RepoProvider, O as ParsedWebhookEvent, v as IncomingWebhookRequest } from "./types-DfiRluvJ.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.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repo-sdk",
3
- "version": "0.1.2",
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",