repo-sdk 0.3.3 → 0.3.5

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.
@@ -1,6 +1,6 @@
1
1
  import { t as RepoError } from "./errors-Cxq_CqYv.js";
2
2
  import { a as isRecord, i as filenameFromContentDisposition, l as decodeCursor, n as commitWebUrlBuilder, s as toCloneUrl, u as encodeCursor } from "./shared-C6m-C4My.js";
3
- import { t as HttpClient } from "./http-DOXOhL_I.js";
3
+ import { t as HttpClient } from "./http-Bm-gcgMf.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 { a as isRecord, c as assertSameOriginUrl, i as filenameFromContentDisposition, l as decodeCursor, n as commitWebUrlBuilder, s as toCloneUrl, t as clampPerPage, u as encodeCursor } from "./shared-C6m-C4My.js";
3
- import { t as HttpClient } from "./http-DOXOhL_I.js";
3
+ import { t as HttpClient } from "./http-Bm-gcgMf.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 { f as stringToBase64, l as decodeCursor, s as toCloneUrl, t as clampPerPage, u as encodeCursor } from "./shared-C6m-C4My.js";
3
- import { t as HttpClient } from "./http-DOXOhL_I.js";
3
+ import { t as HttpClient } from "./http-Bm-gcgMf.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 { a as isRecord, c as assertSameOriginUrl, i as filenameFromContentDisposition, l as decodeCursor, n as commitWebUrlBuilder, o as parseLinkNext, r as encodeRefPath, s as toCloneUrl, t as clampPerPage, u as encodeCursor } from "./shared-C6m-C4My.js";
3
- import { t as HttpClient } from "./http-DOXOhL_I.js";
3
+ import { t as HttpClient } from "./http-Bm-gcgMf.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";
@@ -28,6 +28,13 @@ function reposFromBody(body) {
28
28
  if (isRecord(body) && Array.isArray(body.data)) return body.data;
29
29
  return [];
30
30
  }
31
+ function orgsFromBody(body) {
32
+ if (Array.isArray(body)) return body;
33
+ throw new RepoError("gitea returned an unexpected response for the organizations list", {
34
+ code: "invalid_response",
35
+ provider: "gitea"
36
+ });
37
+ }
31
38
  function toUserNamespace(user) {
32
39
  const slug = user.login ?? user.username ?? "";
33
40
  return {
@@ -216,7 +223,7 @@ function gitea(options) {
216
223
  assertSameOriginUrl("gitea", baseUrl, url);
217
224
  const { data, response } = await http.json(url, { signal: params.signal });
218
225
  return {
219
- data: data.map(toOrgNamespace),
226
+ data: orgsFromBody(data).map(toOrgNamespace),
220
227
  cursor: nextCursor(response)
221
228
  };
222
229
  }
@@ -226,7 +233,7 @@ function gitea(options) {
226
233
  signal: params.signal
227
234
  });
228
235
  return {
229
- data: [toUserNamespace(user), ...orgs.map(toOrgNamespace)],
236
+ data: [toUserNamespace(user), ...orgsFromBody(orgs).map(toOrgNamespace)],
230
237
  cursor: nextCursor(response)
231
238
  };
232
239
  },
package/dist/github.js CHANGED
@@ -1,8 +1,17 @@
1
1
  import { n as codeFromStatus, t as RepoError } from "./errors-Cxq_CqYv.js";
2
2
  import { a as isRecord, c as assertSameOriginUrl, d as bytesToBase64Url, i as filenameFromContentDisposition, l as decodeCursor, n as commitWebUrlBuilder, o as parseLinkNext, r as encodeRefPath, s as toCloneUrl, t as clampPerPage, u as encodeCursor } from "./shared-C6m-C4My.js";
3
- import { t as HttpClient } from "./http-DOXOhL_I.js";
3
+ import { t as HttpClient } from "./http-Bm-gcgMf.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
+ /** GitHub repository ids are positive integers; anything else would only surface as an opaque 422. */
7
+ function toRepositoryId(value) {
8
+ const id = typeof value === "string" && /^\d+$/.test(value) ? Number(value) : value;
9
+ if (typeof id !== "number" || !Number.isSafeInteger(id) || id <= 0) throw new RepoError(`Invalid GitHub repository id: ${String(value)}`, {
10
+ code: "validation",
11
+ provider: "github"
12
+ });
13
+ return id;
14
+ }
6
15
  const RSA_ALGORITHM_IDENTIFIER = [
7
16
  48,
8
17
  13,
@@ -146,7 +155,7 @@ var AppTokenSource = class {
146
155
  this.installationId = options.installationId === void 0 ? void 0 : String(options.installationId);
147
156
  this.owner = options.owner;
148
157
  this.repositories = options.repositories;
149
- this.repositoryIds = options.repositoryIds?.map(Number);
158
+ this.repositoryIds = options.repositoryIds?.map(toRepositoryId);
150
159
  }
151
160
  async getToken() {
152
161
  return (await this.resolveToken()).token;
package/dist/gitlab.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as RepoError } from "./errors-Cxq_CqYv.js";
2
2
  import { a as isRecord, c as assertSameOriginUrl, i as filenameFromContentDisposition, l as decodeCursor, n as commitWebUrlBuilder, o as parseLinkNext, s as toCloneUrl, t as clampPerPage, u as encodeCursor } from "./shared-C6m-C4My.js";
3
- import { t as HttpClient } from "./http-DOXOhL_I.js";
3
+ import { t as HttpClient } from "./http-Bm-gcgMf.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,13 +1,14 @@
1
1
  import { n as codeFromStatus, r as parseRetryAfter, t as RepoError } from "./errors-Cxq_CqYv.js";
2
2
  //#region src/http.ts
3
3
  const USER_AGENT = "repo-sdk";
4
+ const UNSAFE_RELATIVE_PATH = /(^|\/)(\.|%2e){1,2}(\/|$)|[?#\\]/i;
4
5
  var HttpClient = class {
5
6
  options;
6
7
  constructor(options) {
7
8
  this.options = options;
8
9
  }
9
10
  buildUrl(path, query) {
10
- const url = /^https?:\/\//.test(path) ? new URL(path) : new URL(this.options.baseUrl.replace(/\/+$/, "") + "/" + path.replace(/^\/+/, ""));
11
+ const url = /^https?:\/\//.test(path) ? new URL(path) : this.resolveRelativePath(path);
11
12
  if (query) for (const [key, value] of Object.entries(query)) {
12
13
  if (value === void 0) continue;
13
14
  if (Array.isArray(value)) for (const entry of value) url.searchParams.append(key, entry);
@@ -15,6 +16,15 @@ var HttpClient = class {
15
16
  }
16
17
  return url;
17
18
  }
19
+ resolveRelativePath(path) {
20
+ const parsedPath = path.replace(/[\t\n\r]/g, "").replace(/[\u0000- ]+$/, "");
21
+ if (UNSAFE_RELATIVE_PATH.test(parsedPath)) throw new RepoError(`${this.options.provider} request path must not contain '.' or '..' segments, '?', '#' or '\\'`, {
22
+ code: "validation",
23
+ provider: this.options.provider,
24
+ secrets: this.options.secrets?.() ?? []
25
+ });
26
+ return new URL(this.options.baseUrl.replace(/\/+$/, "") + "/" + path.replace(/^\/+/, ""));
27
+ }
18
28
  async raw(path, options = {}) {
19
29
  const url = this.buildUrl(path, options.query);
20
30
  const fetchImpl = this.options.fetchImpl ?? fetch;
package/dist/index.js CHANGED
@@ -48,13 +48,26 @@ function createClient(options) {
48
48
  const { provider } = options;
49
49
  const retryRateLimit = options.retry?.rateLimit ?? true;
50
50
  const maxRetryAfterSeconds = options.retry?.maxRetryAfterSeconds ?? 10;
51
+ function toRepoError(error) {
52
+ if (error instanceof RepoError) return error;
53
+ return new RepoError(`${provider.name} returned a response the SDK could not read`, {
54
+ code: "invalid_response",
55
+ provider: provider.name,
56
+ cause: error
57
+ });
58
+ }
51
59
  async function withRetry(operation) {
52
60
  try {
53
61
  return await operation();
54
- } catch (error) {
55
- if (retryRateLimit && error instanceof RepoError && error.code === "rate_limited" && error.retryAfter !== void 0 && error.retryAfter <= maxRetryAfterSeconds) {
62
+ } catch (caught) {
63
+ const error = toRepoError(caught);
64
+ if (retryRateLimit && error.code === "rate_limited" && error.retryAfter !== void 0 && error.retryAfter <= maxRetryAfterSeconds) {
56
65
  await delay(error.retryAfter * 1e3);
57
- return operation();
66
+ try {
67
+ return await operation();
68
+ } catch (retried) {
69
+ throw toRepoError(retried);
70
+ }
58
71
  }
59
72
  throw error;
60
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repo-sdk",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
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>",