repo-sdk 0.2.0 → 0.2.1

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/dist/github.d.ts CHANGED
@@ -57,6 +57,18 @@ interface ListUserInstallationsParams {
57
57
  cursor?: string;
58
58
  signal?: AbortSignal;
59
59
  }
60
+ interface ListInstallationRequestsParams {
61
+ /** The GitHub App id the installation requests belong to. */
62
+ appId: string | number;
63
+ /** PEM-encoded private key of the app, used to sign the app JWT. */
64
+ privateKey: string;
65
+ /** GHES API root (`/api/v3`); defaults to `https://api.github.com`. */
66
+ baseUrl?: string;
67
+ fetch?: typeof fetch;
68
+ limit?: number;
69
+ cursor?: string;
70
+ signal?: AbortSignal;
71
+ }
60
72
  interface GitHubInstallationAccount {
61
73
  id: string;
62
74
  login: string;
@@ -69,6 +81,16 @@ interface GitHubUserInstallation {
69
81
  account?: GitHubInstallationAccount;
70
82
  raw: unknown;
71
83
  }
84
+ interface GitHubInstallationRequest {
85
+ id: string;
86
+ /** Account (organization or user) the installation was requested for. */
87
+ account?: GitHubInstallationAccount;
88
+ /** ISO 8601 timestamp of when the request was created. */
89
+ createdAt: string;
90
+ /** User who requested the installation. */
91
+ requester?: GitHubInstallationAccount;
92
+ raw: unknown;
93
+ }
72
94
  /**
73
95
  * Lists the GitHub App installations the given user can access
74
96
  * (`GET /user/installations`). Standalone by design: the endpoint requires a
@@ -76,9 +98,17 @@ interface GitHubUserInstallation {
76
98
  * `github` provider authenticates its requests with.
77
99
  */
78
100
  declare function listUserInstallations(params: ListUserInstallationsParams): Promise<Page<GitHubUserInstallation>>;
101
+ /**
102
+ * Lists the pending installation requests of a GitHub App
103
+ * (`GET /app/installation-requests`) — installations an organization owner has
104
+ * yet to approve. Standalone by design: the endpoint is authenticated with an
105
+ * app JWT, a different credential from the installation token the `github`
106
+ * provider authenticates its requests with.
107
+ */
108
+ declare function listInstallationRequests(params: ListInstallationRequestsParams): Promise<Page<GitHubInstallationRequest>>;
79
109
  //#endregion
80
110
  //#region src/providers/github/webhooks.d.ts
81
111
  declare function verifyWebhook(params: VerifyWebhookParams): Promise<boolean>;
82
112
  declare function parseWebhookEvent(input: Request | IncomingWebhookRequest): Promise<ParsedWebhookEvent>;
83
113
  //#endregion
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 };
114
+ export { type GitHubAppAuth, type GitHubAuth, type GitHubInstallationAccount, type GitHubInstallationRequest, type GitHubInstallationToken, type GitHubProviderOptions, type GitHubRepoProvider, type GitHubTokenAuth, type GitHubUserInstallation, type ListInstallationRequestsParams, type ListUserInstallationsParams, type VerifyWebhookParams, commitWebUrl, github, listInstallationRequests, listUserInstallations, parseWebhookEvent, verifyWebhook };
package/dist/github.js CHANGED
@@ -77,7 +77,23 @@ const encoder = new TextEncoder();
77
77
  function base64UrlJson(value) {
78
78
  return bytesToBase64Url(encoder.encode(JSON.stringify(value)));
79
79
  }
80
- async function createJwt(key, appId) {
80
+ /**
81
+ * Mints a short-lived RS256 JWT signed with the app private key — the
82
+ * credential for app-level endpoints (`/app/…`), which installation and user
83
+ * tokens cannot access. Internal to the package: not exported from the
84
+ * `repo-sdk/github` barrel.
85
+ */
86
+ async function createAppJwt(options) {
87
+ let key;
88
+ try {
89
+ key = await importPrivateKey(options.privateKey);
90
+ } catch (error) {
91
+ throw new RepoError("GitHub App private key is not a valid PEM-encoded RSA private key", {
92
+ provider: "github",
93
+ code: "validation",
94
+ cause: error
95
+ });
96
+ }
81
97
  const now = Math.floor(Date.now() / 1e3);
82
98
  const signingInput = `${base64UrlJson({
83
99
  alg: "RS256",
@@ -85,7 +101,7 @@ async function createJwt(key, appId) {
85
101
  })}.${base64UrlJson({
86
102
  iat: now - 60,
87
103
  exp: now + 540,
88
- iss: appId
104
+ iss: String(options.appId)
89
105
  })}`;
90
106
  const signature = await crypto.subtle.sign("RSASSA-PKCS1-v1_5", key, encoder.encode(signingInput));
91
107
  return `${signingInput}.${bytesToBase64Url(new Uint8Array(signature))}`;
@@ -94,8 +110,9 @@ async function createJwt(key, appId) {
94
110
  * Mints and caches GitHub App installation access tokens. A short-lived RS256
95
111
  * JWT (signed with the app private key) authenticates the app-level calls that
96
112
  * resolve the installation and mint installation tokens; the installation token
97
- * is cached and refreshed once it nears expiry. A fresh JWT is minted per token
98
- * refresh (cheap, and refreshes are rare given the ~1h token TTL).
113
+ * is cached and refreshed once it nears expiry. A fresh key import and JWT go
114
+ * into every token refresh (cheap, and refreshes are rare given the ~1h token
115
+ * TTL).
99
116
  */
100
117
  var AppTokenSource = class {
101
118
  kind = "app";
@@ -107,7 +124,6 @@ var AppTokenSource = class {
107
124
  userAgent;
108
125
  installationId;
109
126
  owner;
110
- keyPromise;
111
127
  cached;
112
128
  refresh;
113
129
  constructor(options) {
@@ -153,7 +169,10 @@ var AppTokenSource = class {
153
169
  return this.refresh;
154
170
  }
155
171
  async mintInstallationToken() {
156
- const jwt = await this.createAppJwt();
172
+ const jwt = await createAppJwt({
173
+ appId: this.appId,
174
+ privateKey: this.privateKey
175
+ });
157
176
  const installationId = await this.resolveInstallationId(jwt);
158
177
  const data = await this.appRequest(`/app/installations/${installationId}/access_tokens`, "POST", jwt);
159
178
  const token = {
@@ -199,10 +218,6 @@ var AppTokenSource = class {
199
218
  });
200
219
  return String(installations[0].id);
201
220
  }
202
- createAppJwt() {
203
- if (!this.keyPromise) this.keyPromise = importPrivateKey(this.privateKey);
204
- return this.keyPromise.then((key) => createJwt(key, this.appId));
205
- }
206
221
  async appRequest(path, method, jwt) {
207
222
  const url = joinUrl(this.baseUrl, path);
208
223
  const { fetchImpl } = this;
@@ -864,42 +879,47 @@ function github(options) {
864
879
  }
865
880
  //#endregion
866
881
  //#region src/providers/github/installations.ts
882
+ function toAccount(payload) {
883
+ if (payload?.login === void 0) return void 0;
884
+ return {
885
+ id: String(payload.id),
886
+ login: payload.login,
887
+ kind: payload.type === "Organization" ? "organization" : "user",
888
+ avatarUrl: payload.avatar_url
889
+ };
890
+ }
867
891
  function toInstallation(payload) {
868
- const account = payload.account;
869
892
  return {
870
893
  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
- },
894
+ account: toAccount(payload.account),
877
895
  raw: payload
878
896
  };
879
897
  }
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;
898
+ function toInstallationRequest(payload) {
899
+ return {
900
+ id: String(payload.id),
901
+ account: toAccount(payload.account),
902
+ createdAt: payload.created_at,
903
+ requester: toAccount(payload.requester),
904
+ raw: payload
905
+ };
906
+ }
907
+ async function listPage(bearer, defaultPath, params, toItems) {
888
908
  const baseUrl = params.baseUrl ?? "https://api.github.com";
889
909
  const http = new HttpClient({
890
910
  provider: "github",
891
911
  baseUrl,
892
912
  fetchImpl: params.fetch,
893
913
  authHeaders: () => ({
894
- Authorization: `Bearer ${token}`,
914
+ Authorization: `Bearer ${bearer}`,
895
915
  Accept: "application/vnd.github+json",
896
916
  "X-GitHub-Api-Version": API_VERSION,
897
917
  "User-Agent": USER_AGENT
898
918
  }),
899
919
  mapError,
900
- secrets: () => [token]
920
+ secrets: () => [bearer]
901
921
  });
902
- let path = "/user/installations";
922
+ let path = defaultPath;
903
923
  let query;
904
924
  if (params.cursor) {
905
925
  const { url } = decodeCursor("github", params.cursor);
@@ -910,10 +930,32 @@ async function listUserInstallations(params) {
910
930
  signal: params.signal
911
931
  });
912
932
  return {
913
- data: (data.installations ?? []).map(toInstallation),
933
+ data: toItems(data),
914
934
  cursor: nextCursor(response)
915
935
  };
916
936
  }
937
+ /**
938
+ * Lists the GitHub App installations the given user can access
939
+ * (`GET /user/installations`). Standalone by design: the endpoint requires a
940
+ * user access token, a different credential from the installation token the
941
+ * `github` provider authenticates its requests with.
942
+ */
943
+ async function listUserInstallations(params) {
944
+ return listPage(params.token, "/user/installations", params, (payload) => (payload.installations ?? []).map(toInstallation));
945
+ }
946
+ /**
947
+ * Lists the pending installation requests of a GitHub App
948
+ * (`GET /app/installation-requests`) — installations an organization owner has
949
+ * yet to approve. Standalone by design: the endpoint is authenticated with an
950
+ * app JWT, a different credential from the installation token the `github`
951
+ * provider authenticates its requests with.
952
+ */
953
+ async function listInstallationRequests(params) {
954
+ return listPage(await createAppJwt({
955
+ appId: params.appId,
956
+ privateKey: params.privateKey
957
+ }), "/app/installation-requests", params, (payload) => payload.map(toInstallationRequest));
958
+ }
917
959
  //#endregion
918
960
  //#region src/providers/github/webhooks.ts
919
961
  function verifyWebhook(params) {
@@ -949,4 +991,4 @@ async function parseWebhookEvent(input) {
949
991
  };
950
992
  }
951
993
  //#endregion
952
- export { commitWebUrl, github, listUserInstallations, parseWebhookEvent, verifyWebhook };
994
+ export { commitWebUrl, github, listInstallationRequests, listUserInstallations, parseWebhookEvent, verifyWebhook };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repo-sdk",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
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>",