repo-sdk 0.2.1 → 0.3.0
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 +142 -19
- package/dist/azure-devops.d.ts +1 -1
- package/dist/azure-devops.js +23 -9
- package/dist/bitbucket.d.ts +1 -1
- package/dist/bitbucket.js +17 -5
- package/dist/git-http.d.ts +1 -1
- package/dist/git-http.js +17 -8
- package/dist/gitea.d.ts +1 -1
- package/dist/gitea.js +11 -5
- package/dist/github.d.ts +1 -1
- package/dist/github.js +37 -23
- package/dist/gitlab.d.ts +1 -1
- package/dist/gitlab.js +20 -12
- package/dist/{http-C3hAQeY0.js → http-BcC1vIan.js} +1 -32
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -0
- package/dist/{pagination-CWWHSu8I.js → shared-C6m-C4My.js} +51 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +12 -4
- package/dist/{types-B9oMHTeP.d.ts → types-E47xhSNA.d.ts} +20 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,7 +4,16 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/repo-sdk)
|
|
5
5
|
[](https://github.com/capawesome-team/repo-sdk/blob/main/LICENSE)
|
|
6
6
|
|
|
7
|
-
A unified, normalized, zero-dependency, edge-compatible TypeScript SDK over GitHub, GitLab, Bitbucket Cloud, Azure DevOps, and Gitea — plus generic git smart-HTTP remotes. Write your
|
|
7
|
+
A unified, normalized, zero-dependency, edge-compatible TypeScript SDK over [GitHub](https://github.com), [GitLab](https://gitlab.com), [Bitbucket Cloud](https://bitbucket.org), [Azure DevOps](https://azure.microsoft.com/products/devops), and [Gitea](https://about.gitea.com) — plus generic git smart-HTTP remotes. Write your repository, commit, branch, tag, ref-resolution, download and webhook logic once against one normalized API — built on raw `fetch` and Web Crypto, so it runs on Node, Cloudflare Workers, and other Web-standard runtimes.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **One API, eight namespaces** — users, namespaces, repos, commits, branches, tags, refs, and webhooks behind a single interface.
|
|
12
|
+
- **Capability gating** — providers differ. The client reports what the active provider supports and throws `unsupported` instead of silently dropping an option.
|
|
13
|
+
- **Ref resolution that follows git** — `refs.resolve` accepts a branch, a tag, a fully-qualified `refs/…`, `HEAD`, or an abbreviated SHA and returns one normalized match, with tags shadowing branches exactly like `git rev-parse`.
|
|
14
|
+
- **Webhook verify & parse** — standalone per-provider helpers that take a Web-standard `Request`. No client, no credentials. `detectWebhookProvider` routes a shared endpoint to the right pair.
|
|
15
|
+
- **Zero dependencies** — `fetch` and Web Crypto only. No `node:*` imports, so it runs on Cloudflare Workers without `nodejs_compat` — including RS256 JWT signing for GitHub App auth.
|
|
16
|
+
- **Tree-shakable and typed** — each provider ships on its own subpath, so unused providers never reach your bundle. Every model is normalized (IDs as strings, dates as `Date`, refs fully qualified) and keeps the untouched provider payload on `raw`.
|
|
8
17
|
|
|
9
18
|
## Installation
|
|
10
19
|
|
|
@@ -14,6 +23,8 @@ npm install repo-sdk
|
|
|
14
23
|
|
|
15
24
|
## Quickstart
|
|
16
25
|
|
|
26
|
+
Create a client from the package root and a provider factory from its subpath.
|
|
27
|
+
|
|
17
28
|
```ts
|
|
18
29
|
import { createClient } from 'repo-sdk';
|
|
19
30
|
import { github } from 'repo-sdk/github';
|
|
@@ -21,42 +32,154 @@ import { github } from 'repo-sdk/github';
|
|
|
21
32
|
const client = createClient({
|
|
22
33
|
provider: github({ auth: { token: process.env.GITHUB_TOKEN! } }),
|
|
23
34
|
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Resolve a ref — a branch, tag, SHA, or `HEAD` — to a single commit, and page through history.
|
|
24
38
|
|
|
25
|
-
|
|
39
|
+
```ts
|
|
26
40
|
const head = await client.commits.get({ repo: 'capawesome-team/repo-sdk', ref: 'main' });
|
|
27
41
|
console.log(head.sha, head.message);
|
|
28
42
|
|
|
29
|
-
|
|
30
|
-
const { data: commits } = await client.commits.list({
|
|
43
|
+
const { data: commits, cursor } = await client.commits.list({
|
|
31
44
|
repo: 'capawesome-team/repo-sdk',
|
|
32
45
|
ref: 'main',
|
|
33
46
|
limit: 20,
|
|
34
47
|
});
|
|
35
48
|
```
|
|
36
49
|
|
|
37
|
-
|
|
50
|
+
Find out what a user-supplied ref actually is before you act on it.
|
|
38
51
|
|
|
39
|
-
|
|
52
|
+
```ts
|
|
53
|
+
const match = await client.refs.resolve({ repo: 'capawesome-team/repo-sdk', ref: 'v1.0.0' });
|
|
54
|
+
match.type; // 'branch' | 'tag' | 'commit'
|
|
55
|
+
match.ref; // 'refs/tags/v1.0.0'
|
|
56
|
+
match.sha; // peeled to the commit SHA for annotated tags
|
|
57
|
+
```
|
|
40
58
|
|
|
41
|
-
|
|
59
|
+
Download the code at that ref, as a stream or as a credential-bearing clone URL for the `git` CLI.
|
|
42
60
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
-
|
|
46
|
-
|
|
61
|
+
```ts
|
|
62
|
+
const archive = await client.repos.downloadArchive({
|
|
63
|
+
repo: 'capawesome-team/repo-sdk',
|
|
64
|
+
ref: match.sha,
|
|
65
|
+
format: 'tar.gz',
|
|
66
|
+
});
|
|
67
|
+
await new Response(archive.stream).arrayBuffer();
|
|
68
|
+
|
|
69
|
+
const { url } = await client.repos.getCloneUrl({ repo: 'capawesome-team/repo-sdk' });
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Verify and handle the webhook. The helpers take the `Request` itself — no client, no credentials.
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { parseWebhookEvent, verifyWebhook } from 'repo-sdk/github';
|
|
76
|
+
|
|
77
|
+
export async function POST(request: Request): Promise<Response> {
|
|
78
|
+
if (!(await verifyWebhook({ request, secret: process.env.WEBHOOK_SECRET! }))) {
|
|
79
|
+
return new Response('invalid signature', { status: 401 });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const event = await parseWebhookEvent(request);
|
|
83
|
+
|
|
84
|
+
switch (event.type) {
|
|
85
|
+
case 'push':
|
|
86
|
+
// event.headCommitSha is undefined when the push deleted the ref.
|
|
87
|
+
break;
|
|
88
|
+
case 'tag_push':
|
|
89
|
+
// event.ref is always fully qualified — refs/tags/v1.0.0.
|
|
90
|
+
break;
|
|
91
|
+
case 'release':
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return new Response(null, { status: 204 });
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## API at a glance
|
|
100
|
+
|
|
101
|
+
| Namespace | Methods |
|
|
102
|
+
| ------------ | -------------------------------------------------------------------------------------- |
|
|
103
|
+
| `users` | `me` |
|
|
104
|
+
| `namespaces` | `list` · `listAll` |
|
|
105
|
+
| `repos` | `list` · `listAll` · `get` · `downloadArchive` · `getCloneCredentials` · `getCloneUrl` |
|
|
106
|
+
| `commits` | `list` · `listAll` · `get` |
|
|
107
|
+
| `branches` | `list` · `listAll` · `get` |
|
|
108
|
+
| `tags` | `list` · `listAll` · `get` |
|
|
109
|
+
| `refs` | `resolve` · `search` |
|
|
110
|
+
| `webhooks` | `create` · `list` · `get` · `update` · `delete` |
|
|
111
|
+
|
|
112
|
+
Every list returns an opaque cursor and has a `listAll` async generator that walks the pages for you. Cursors are provider-tagged and origin-checked, so a forged one cannot redirect an authenticated request. Every method accepts a `signal` for cancellation. Rate-limited requests are retried once when the provider's `Retry-After` fits the budget (10 seconds by default, configurable via `retry`).
|
|
113
|
+
|
|
114
|
+
Webhook payload helpers — `verifyWebhook` and `parseWebhookEvent` — are exported from the provider subpaths and take no credential beyond the shared secret, so they run in a handler with no client at all. `detectWebhookProvider` from the core entry identifies the sender when one endpoint serves several providers.
|
|
47
115
|
|
|
48
116
|
## Providers
|
|
49
117
|
|
|
50
118
|
| Provider | Import | Notes |
|
|
51
119
|
| ------------ | ----------------------- | ---------------------------------------------------------------- |
|
|
52
|
-
| GitHub | `repo-sdk/github` |
|
|
53
|
-
| GitLab | `repo-sdk/gitlab` |
|
|
54
|
-
| Bitbucket | `repo-sdk/bitbucket` | Bitbucket Cloud
|
|
55
|
-
| Azure DevOps | `repo-sdk/azure-devops` | PAT or Entra ID
|
|
56
|
-
| Gitea | `repo-sdk/gitea` | Gitea ≥ 1.20 and Forgejo
|
|
57
|
-
| Generic git | `repo-sdk/git-http` | Any git smart-HTTP remote
|
|
120
|
+
| GitHub | `repo-sdk/github` | Personal access token or GitHub App installation auth. |
|
|
121
|
+
| GitLab | `repo-sdk/gitlab` | Personal, group, project, or OAuth token. |
|
|
122
|
+
| Bitbucket | `repo-sdk/bitbucket` | Bitbucket Cloud. Atlassian API token or access token. |
|
|
123
|
+
| Azure DevOps | `repo-sdk/azure-devops` | Organization-scoped. PAT or Entra ID token. |
|
|
124
|
+
| Gitea | `repo-sdk/gitea` | Gitea ≥ 1.20 and Forgejo. |
|
|
125
|
+
| Generic git | `repo-sdk/git-http` | Any git smart-HTTP remote. Ref discovery and clone URLs only. |
|
|
126
|
+
| Testing | `repo-sdk/testing` | Seedable in-memory provider for your test suite. No credentials. |
|
|
58
127
|
|
|
59
|
-
Self-hosted deployments — GitHub Enterprise Server, GitLab self-managed, Azure DevOps Server, and Gitea/Forgejo
|
|
128
|
+
Every factory accepts an injectable `fetch`, and every token can be a static string or a `tokenProvider` that mints one per request and is re-invoked once on a 401. Self-hosted deployments — GitHub Enterprise Server, GitLab self-managed, Azure DevOps Server, and Gitea/Forgejo — are supported via `baseUrl`. Providers do not support the same feature set — `client.capabilities` tells you what the active one can do, and the full breakdown lives in the [capability matrix](https://repo-sdk.dev/docs/reference/capability-matrix).
|
|
129
|
+
|
|
130
|
+
## Testing
|
|
131
|
+
|
|
132
|
+
`repo-sdk/testing` ships a seedable in-memory provider you hand to `createClient` like any other — no account, no tokens, no `fetch` stubs, no throwaway repositories.
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import { createClient } from 'repo-sdk';
|
|
136
|
+
import { createInMemoryProvider } from 'repo-sdk/testing';
|
|
137
|
+
|
|
138
|
+
const client = createClient({
|
|
139
|
+
provider: createInMemoryProvider({
|
|
140
|
+
repositories: { 'acme/app': { name: 'app', namespace: 'acme', defaultBranch: 'main' } },
|
|
141
|
+
branches: { 'acme/app': [{ name: 'main', sha: 'c1' }] },
|
|
142
|
+
commits: { 'acme/app': [{ sha: 'c1', message: 'Initial commit' }] },
|
|
143
|
+
}),
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
It reports every capability enabled by default, and a second options argument simulates a specific provider's identity and capability gaps — so code that branches on `client.capabilities` is testable. Its page size is deliberately small, so your cursor pagination gets exercised, and `provider.state` exposes the data as plain maps for assertions.
|
|
148
|
+
|
|
149
|
+
## Errors
|
|
150
|
+
|
|
151
|
+
Every failure is a `RepoError` with a closed `code` union: `unauthorized`, `forbidden`, `not_found`, `rate_limited`, `validation`, `unsupported`, `provider_error`, `network_error`.
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import { RepoError } from 'repo-sdk';
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
await client.repos.downloadArchive({ repo: 'acme/app', ref: 'main', format: 'tar.gz' });
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if (error instanceof RepoError && error.code === 'unsupported') {
|
|
160
|
+
// Azure DevOps serves zip only — fall back instead of failing the job.
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Errors also carry `provider`, `status`, `retryAfter` and `retryable`; `cause` is the underlying JS `Error`. Token values are redacted from every message, so a leaked stack trace cannot leak a credential. The deliberate exceptions are `repos.getCloneUrl`, which returns a URL with the credential embedded because that is what `git clone` needs, and `repos.getCloneCredentials`, which returns that credential next to a credential-free URL for a git credential helper — treat both as secrets.
|
|
166
|
+
|
|
167
|
+
## Runtime support
|
|
168
|
+
|
|
169
|
+
Node.js ≥ 20, Cloudflare Workers (without `nodejs_compat`), Deno, Bun, Vercel Edge, and any other runtime with `fetch`, Web Crypto and `TextEncoder`. ESM only.
|
|
170
|
+
|
|
171
|
+
## Documentation
|
|
172
|
+
|
|
173
|
+
Full documentation lives at **[repo-sdk.dev](https://repo-sdk.dev)** ([docs](https://repo-sdk.dev/docs)):
|
|
174
|
+
|
|
175
|
+
- [Quickstart](https://repo-sdk.dev/docs/quickstart)
|
|
176
|
+
- [Concepts](https://repo-sdk.dev/docs/concepts/client-and-providers) — clients, repositories and namespaces, capabilities, pagination, errors
|
|
177
|
+
- [Authentication](https://repo-sdk.dev/docs/authentication) — tokens, GitHub Apps, Entra ID, and the scopes each provider needs
|
|
178
|
+
- [Guides](https://repo-sdk.dev/docs/guides/resolving-refs) — resolving refs, commits, branches, tags, downloading code, managing and receiving webhooks
|
|
179
|
+
- [Testing](https://repo-sdk.dev/docs/testing)
|
|
180
|
+
- [Capability matrix](https://repo-sdk.dev/docs/reference/capability-matrix)
|
|
181
|
+
- [Provider support](https://repo-sdk.dev/docs/reference/provider-support)
|
|
182
|
+
- [Error codes](https://repo-sdk.dev/docs/reference/error-codes)
|
|
60
183
|
|
|
61
184
|
## Development
|
|
62
185
|
|
|
@@ -69,7 +192,7 @@ npm install
|
|
|
69
192
|
| Script | Description |
|
|
70
193
|
| -------------------- | --------------------------------------------------------------- |
|
|
71
194
|
| `npm run build` | Build with tsdown |
|
|
72
|
-
| `npm test` | Run the unit + contract test suite
|
|
195
|
+
| `npm test` | Run the unit + provider contract test suite |
|
|
73
196
|
| `npm run test:watch` | Run tests in watch mode |
|
|
74
197
|
| `npm run test:live` | Gated live provider tests (env vars documented in CONTRIBUTING) |
|
|
75
198
|
| `npm run typecheck` | Type-check without emitting |
|
package/dist/azure-devops.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as ParsedWebhookEvent, H as TokenProvider, L as RepoProvider, b as IncomingWebhookRequest } from "./types-E47xhSNA.js";
|
|
2
2
|
import { t as VerifyWebhookParams$1 } from "./verify-p6JsExms.js";
|
|
3
3
|
//#region src/providers/azure-devops/auth.d.ts
|
|
4
4
|
/**
|
package/dist/azure-devops.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as RepoError } from "./errors-Cxq_CqYv.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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-BcC1vIan.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
|
/**
|
|
@@ -215,6 +215,25 @@ function azureDevOps(options) {
|
|
|
215
215
|
});
|
|
216
216
|
return ref;
|
|
217
217
|
}
|
|
218
|
+
async function getCloneCredentials(params) {
|
|
219
|
+
const { project, repository } = splitRepo(params.repo);
|
|
220
|
+
const url = `https://${new URL(baseUrl).host}/${enc(options.organization)}/${enc(project)}/_git/${enc(repository)}`;
|
|
221
|
+
if ("pat" in auth) return {
|
|
222
|
+
password: auth.pat,
|
|
223
|
+
url,
|
|
224
|
+
username: "pat"
|
|
225
|
+
};
|
|
226
|
+
if ("accessToken" in auth) return {
|
|
227
|
+
password: auth.accessToken,
|
|
228
|
+
url,
|
|
229
|
+
username: "oauth2"
|
|
230
|
+
};
|
|
231
|
+
return {
|
|
232
|
+
password: await auth.tokenProvider({ forceRefresh: false }),
|
|
233
|
+
url,
|
|
234
|
+
username: "oauth2"
|
|
235
|
+
};
|
|
236
|
+
}
|
|
218
237
|
return {
|
|
219
238
|
name: "azure-devops",
|
|
220
239
|
capabilities: CAPABILITIES,
|
|
@@ -393,14 +412,9 @@ function azureDevOps(options) {
|
|
|
393
412
|
filename: filenameFromContentDisposition(response.headers.get("content-disposition"))
|
|
394
413
|
};
|
|
395
414
|
},
|
|
415
|
+
getCloneCredentials,
|
|
396
416
|
async getCloneUrl(params) {
|
|
397
|
-
|
|
398
|
-
const host = new URL(baseUrl).host;
|
|
399
|
-
const repoPath = `${enc(options.organization)}/${enc(project)}/_git/${enc(repository)}`;
|
|
400
|
-
if ("pat" in auth) return { url: `https://pat:${encodeURIComponent(auth.pat)}@${host}/${repoPath}` };
|
|
401
|
-
if ("accessToken" in auth) return { url: `https://oauth2:${encodeURIComponent(auth.accessToken)}@${host}/${repoPath}` };
|
|
402
|
-
const token = await auth.tokenProvider({ forceRefresh: false });
|
|
403
|
-
return { url: `https://oauth2:${encodeURIComponent(token)}@${host}/${repoPath}` };
|
|
417
|
+
return toCloneUrl(await getCloneCredentials(params));
|
|
404
418
|
},
|
|
405
419
|
async createWebhook(params) {
|
|
406
420
|
if (!params.events.includes("push") || !params.events.includes("tag_push")) throw new RepoError("Azure DevOps delivers branch and tag pushes together via a single 'git.push' subscription and cannot filter to a subset; request both 'push' and 'tag_push'", {
|
package/dist/bitbucket.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as ParsedWebhookEvent, H as TokenProvider, L as RepoProvider, b as IncomingWebhookRequest } from "./types-E47xhSNA.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
|
-
import {
|
|
3
|
-
import {
|
|
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-BcC1vIan.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";
|
|
@@ -167,6 +167,19 @@ function bitbucket(options) {
|
|
|
167
167
|
function hookPath(repo, id) {
|
|
168
168
|
return `${repoPath(repo)}/hooks/${encodeURIComponent(id)}`;
|
|
169
169
|
}
|
|
170
|
+
async function getCloneCredentials(params) {
|
|
171
|
+
const url = `https://${GIT_HOST}/${params.repo}.git`;
|
|
172
|
+
if (isApiTokenAuth(auth)) return {
|
|
173
|
+
password: auth.apiToken,
|
|
174
|
+
url,
|
|
175
|
+
username: "x-bitbucket-api-token-auth"
|
|
176
|
+
};
|
|
177
|
+
return {
|
|
178
|
+
password: await currentToken(false),
|
|
179
|
+
url,
|
|
180
|
+
username: "x-token-auth"
|
|
181
|
+
};
|
|
182
|
+
}
|
|
170
183
|
return {
|
|
171
184
|
name: "bitbucket",
|
|
172
185
|
capabilities: CAPABILITIES,
|
|
@@ -338,10 +351,9 @@ function bitbucket(options) {
|
|
|
338
351
|
filename: filenameFromContentDisposition(response.headers.get("content-disposition"))
|
|
339
352
|
};
|
|
340
353
|
},
|
|
354
|
+
getCloneCredentials,
|
|
341
355
|
async getCloneUrl(params) {
|
|
342
|
-
|
|
343
|
-
const token = await currentToken(false);
|
|
344
|
-
return { url: `https://x-token-auth:${encodeURIComponent(token)}@${GIT_HOST}/${params.repo}.git` };
|
|
356
|
+
return toCloneUrl(await getCloneCredentials(params));
|
|
345
357
|
},
|
|
346
358
|
async createWebhook(params) {
|
|
347
359
|
const { data } = await http.json(`${repoPath(params.repo)}/hooks`, {
|
package/dist/git-http.d.ts
CHANGED
package/dist/git-http.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as RepoError } from "./errors-Cxq_CqYv.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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-BcC1vIan.js";
|
|
4
4
|
//#region src/providers/git-http/index.ts
|
|
5
5
|
const CAPABILITIES = {
|
|
6
6
|
userProfile: false,
|
|
@@ -192,6 +192,19 @@ function gitHttp(options = {}) {
|
|
|
192
192
|
}) : void 0
|
|
193
193
|
};
|
|
194
194
|
}
|
|
195
|
+
async function getCloneCredentials(params) {
|
|
196
|
+
const url = normalizeRepoUrl(params.repo);
|
|
197
|
+
if (!auth) return {
|
|
198
|
+
password: null,
|
|
199
|
+
url,
|
|
200
|
+
username: null
|
|
201
|
+
};
|
|
202
|
+
return {
|
|
203
|
+
password: await currentSecret(false),
|
|
204
|
+
url,
|
|
205
|
+
username
|
|
206
|
+
};
|
|
207
|
+
}
|
|
195
208
|
return {
|
|
196
209
|
name: "git-http",
|
|
197
210
|
capabilities: CAPABILITIES,
|
|
@@ -288,13 +301,9 @@ function gitHttp(options = {}) {
|
|
|
288
301
|
async downloadArchive() {
|
|
289
302
|
throw unsupported("archive downloads");
|
|
290
303
|
},
|
|
304
|
+
getCloneCredentials,
|
|
291
305
|
async getCloneUrl(params) {
|
|
292
|
-
|
|
293
|
-
if (!auth) return { url: repoUrl };
|
|
294
|
-
const url = new URL(repoUrl);
|
|
295
|
-
url.username = username;
|
|
296
|
-
url.password = await currentSecret(false);
|
|
297
|
-
return { url: url.toString() };
|
|
306
|
+
return toCloneUrl(await getCloneCredentials(params));
|
|
298
307
|
},
|
|
299
308
|
async createWebhook() {
|
|
300
309
|
throw unsupported("webhooks");
|
package/dist/gitea.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as ParsedWebhookEvent, H as TokenProvider, L as RepoProvider, b as IncomingWebhookRequest } from "./types-E47xhSNA.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
|
-
import {
|
|
3
|
-
import {
|
|
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-BcC1vIan.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";
|
|
@@ -189,6 +189,13 @@ function gitea(options) {
|
|
|
189
189
|
function repoPath(repo) {
|
|
190
190
|
return `/repos/${repo}`;
|
|
191
191
|
}
|
|
192
|
+
async function getCloneCredentials(params) {
|
|
193
|
+
return {
|
|
194
|
+
password: null,
|
|
195
|
+
url: `https://${new URL(baseUrl).host}/${params.repo}.git`,
|
|
196
|
+
username: await currentToken(false)
|
|
197
|
+
};
|
|
198
|
+
}
|
|
192
199
|
return {
|
|
193
200
|
name: "gitea",
|
|
194
201
|
capabilities: CAPABILITIES,
|
|
@@ -400,10 +407,9 @@ function gitea(options) {
|
|
|
400
407
|
filename: filenameFromContentDisposition(response.headers.get("content-disposition"))
|
|
401
408
|
};
|
|
402
409
|
},
|
|
410
|
+
getCloneCredentials,
|
|
403
411
|
async getCloneUrl(params) {
|
|
404
|
-
|
|
405
|
-
const token = await currentToken(false);
|
|
406
|
-
return { url: `https://${encodeURIComponent(token)}@${host}/${params.repo}.git` };
|
|
412
|
+
return toCloneUrl(await getCloneCredentials(params));
|
|
407
413
|
},
|
|
408
414
|
async createWebhook(params) {
|
|
409
415
|
const { data } = await http.json(`${repoPath(params.repo)}/hooks`, {
|
package/dist/github.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as ParsedWebhookEvent, H as TokenProvider, L as RepoProvider, b as IncomingWebhookRequest, k as Page } from "./types-E47xhSNA.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 {
|
package/dist/github.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as codeFromStatus, t as RepoError } from "./errors-Cxq_CqYv.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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-BcC1vIan.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 = [
|
|
@@ -501,6 +501,15 @@ function github(options) {
|
|
|
501
501
|
raw: owner
|
|
502
502
|
}] };
|
|
503
503
|
}
|
|
504
|
+
function getCloneCredentials(params) {
|
|
505
|
+
const host = gitHostFromBaseUrl(baseUrl);
|
|
506
|
+
return tokenSource.getTokenWithExpiry().then(({ token, expiresAt }) => ({
|
|
507
|
+
expiresAt,
|
|
508
|
+
password: token,
|
|
509
|
+
url: `https://${host}/${params.repo}.git`,
|
|
510
|
+
username: "x-access-token"
|
|
511
|
+
}));
|
|
512
|
+
}
|
|
504
513
|
return {
|
|
505
514
|
name: "github",
|
|
506
515
|
capabilities: {
|
|
@@ -580,6 +589,17 @@ function github(options) {
|
|
|
580
589
|
};
|
|
581
590
|
}
|
|
582
591
|
if (params.namespace) {
|
|
592
|
+
if (tokenSource.kind === "app") {
|
|
593
|
+
const namespaceLower = params.namespace.toLowerCase();
|
|
594
|
+
const { data, response } = await http.json("/installation/repositories", {
|
|
595
|
+
query: { per_page: perPage },
|
|
596
|
+
signal: params.signal
|
|
597
|
+
});
|
|
598
|
+
return {
|
|
599
|
+
data: reposFromBody(data).filter((repo) => repo.owner.login.toLowerCase() === namespaceLower).map(mapRepository),
|
|
600
|
+
cursor: nextCursor(response)
|
|
601
|
+
};
|
|
602
|
+
}
|
|
583
603
|
try {
|
|
584
604
|
const { data, response } = await http.json(`/orgs/${encodeURIComponent(params.namespace)}/repos`, {
|
|
585
605
|
query: { per_page: perPage },
|
|
@@ -592,21 +612,18 @@ function github(options) {
|
|
|
592
612
|
} catch (error) {
|
|
593
613
|
if (!(error instanceof RepoError && error.code === "not_found")) throw error;
|
|
594
614
|
}
|
|
595
|
-
if (
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
cursor: nextCursor(response)
|
|
608
|
-
};
|
|
609
|
-
}
|
|
615
|
+
if ((await getLogin(params.signal)).toLowerCase() === params.namespace.toLowerCase()) {
|
|
616
|
+
const { data, response } = await http.json("/user/repos", {
|
|
617
|
+
query: {
|
|
618
|
+
affiliation: "owner",
|
|
619
|
+
per_page: perPage
|
|
620
|
+
},
|
|
621
|
+
signal: params.signal
|
|
622
|
+
});
|
|
623
|
+
return {
|
|
624
|
+
data: data.map(mapRepository),
|
|
625
|
+
cursor: nextCursor(response)
|
|
626
|
+
};
|
|
610
627
|
}
|
|
611
628
|
const { data, response } = await http.json(`/users/${encodeURIComponent(params.namespace)}/repos`, {
|
|
612
629
|
query: { per_page: perPage },
|
|
@@ -797,12 +814,9 @@ function github(options) {
|
|
|
797
814
|
filename: filenameFromContentDisposition(archiveResponse.headers.get("content-disposition"))
|
|
798
815
|
};
|
|
799
816
|
},
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
return
|
|
803
|
-
url: `https://x-access-token:${token}@${host}/${params.repo}.git`,
|
|
804
|
-
expiresAt
|
|
805
|
-
}));
|
|
817
|
+
getCloneCredentials,
|
|
818
|
+
async getCloneUrl(params) {
|
|
819
|
+
return toCloneUrl(await getCloneCredentials(params));
|
|
806
820
|
},
|
|
807
821
|
async createWebhook(params) {
|
|
808
822
|
const { data } = await http.json(`${repoPath(params.repo)}/hooks`, {
|
package/dist/gitlab.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as ParsedWebhookEvent, H as TokenProvider, L as RepoProvider, b as IncomingWebhookRequest } from "./types-E47xhSNA.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
|
-
import {
|
|
3
|
-
import {
|
|
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-BcC1vIan.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";
|
|
@@ -161,9 +161,6 @@ function assertActiveSupported(active) {
|
|
|
161
161
|
provider: "gitlab"
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
|
-
function injectCredentials(cloneUrl, token) {
|
|
165
|
-
return cloneUrl.replace("://", `://oauth2:${encodeURIComponent(token)}@`);
|
|
166
|
-
}
|
|
167
164
|
function gitHostFromBaseUrl(baseUrl) {
|
|
168
165
|
return new URL(baseUrl).host;
|
|
169
166
|
}
|
|
@@ -205,6 +202,22 @@ function gitlab(options) {
|
|
|
205
202
|
function hooksPath(repo) {
|
|
206
203
|
return `/projects/${projectId(repo)}/hooks`;
|
|
207
204
|
}
|
|
205
|
+
async function getCloneCredentials(params) {
|
|
206
|
+
const password = await tokenSource.getToken();
|
|
207
|
+
if (isNumericId(params.repo)) {
|
|
208
|
+
const { data } = await http.json(`/projects/${params.repo}`, { signal: params.signal });
|
|
209
|
+
return {
|
|
210
|
+
password,
|
|
211
|
+
url: data.http_url_to_repo,
|
|
212
|
+
username: "oauth2"
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
password,
|
|
217
|
+
url: `https://${gitHostFromBaseUrl(baseUrl)}/${params.repo}.git`,
|
|
218
|
+
username: "oauth2"
|
|
219
|
+
};
|
|
220
|
+
}
|
|
208
221
|
return {
|
|
209
222
|
name: "gitlab",
|
|
210
223
|
capabilities: CAPABILITIES,
|
|
@@ -433,14 +446,9 @@ function gitlab(options) {
|
|
|
433
446
|
filename: filenameFromContentDisposition(response.headers.get("content-disposition"))
|
|
434
447
|
};
|
|
435
448
|
},
|
|
449
|
+
getCloneCredentials,
|
|
436
450
|
async getCloneUrl(params) {
|
|
437
|
-
|
|
438
|
-
if (isNumericId(params.repo)) {
|
|
439
|
-
const { data } = await http.json(`/projects/${params.repo}`, { signal: params.signal });
|
|
440
|
-
return { url: injectCredentials(data.http_url_to_repo, token) };
|
|
441
|
-
}
|
|
442
|
-
const host = gitHostFromBaseUrl(baseUrl);
|
|
443
|
-
return { url: `https://oauth2:${encodeURIComponent(token)}@${host}/${params.repo}.git` };
|
|
451
|
+
return toCloneUrl(await getCloneCredentials(params));
|
|
444
452
|
},
|
|
445
453
|
async createWebhook(params) {
|
|
446
454
|
assertActiveSupported(params.active);
|
|
@@ -1,35 +1,4 @@
|
|
|
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
|
|
33
2
|
//#region src/http.ts
|
|
34
3
|
const USER_AGENT = "repo-sdk";
|
|
35
4
|
var HttpClient = class {
|
|
@@ -104,4 +73,4 @@ var HttpClient = class {
|
|
|
104
73
|
}
|
|
105
74
|
};
|
|
106
75
|
//#endregion
|
|
107
|
-
export {
|
|
76
|
+
export { HttpClient as t };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as ParsedWebhookEvent, B as SearchRefsParams, C as ListNamespacesParams, D as Namespace, E as ListWebhooksParams, F as RefType, G as Webhook, H as TokenProvider, I as RepoCapabilities, K as WebhookEventType, L as RepoProvider, M as ProviderRefMatch, N as ProviderSearchRefsParams, O as NamespaceKind, P as RefMatch, R as Repository, S as ListCommitsParams, T as ListTagsParams, U as UpdateWebhookParams, V as Tag, W as UserRef, _ as GetTagParams, a as CloneCredentials, b as IncomingWebhookRequest, c as CreateWebhookParams, d as GetAuthenticatedUserParams, f as GetBranchParams, g as GetRepositoryParams, h as GetCommitParams, i as Branch, j as ProviderName, k as Page, l as DeleteWebhookParams, m as GetCloneUrlParams, n as ArchiveFormat, o as CloneUrl, p as GetCloneCredentialsParams, q as WebhookVerificationMethod, r as AuthenticatedUser, s as Commit, t as Archive, u as DownloadArchiveParams, v as GetWebhookParams, w as ListRepositoriesParams, x as ListBranchesParams, y as GitActor, z as ResolveRefParams } from "./types-E47xhSNA.js";
|
|
2
2
|
//#region src/client.d.ts
|
|
3
3
|
interface RetryOptions {
|
|
4
4
|
rateLimit?: boolean;
|
|
@@ -23,6 +23,7 @@ interface RepoClient {
|
|
|
23
23
|
listAll(params?: Omit<ListRepositoriesParams, 'cursor'>): AsyncGenerator<Repository, void>;
|
|
24
24
|
get(params: GetRepositoryParams): Promise<Repository>;
|
|
25
25
|
downloadArchive(params: DownloadArchiveParams): Promise<Archive>;
|
|
26
|
+
getCloneCredentials(params: GetCloneCredentialsParams): Promise<CloneCredentials>;
|
|
26
27
|
getCloneUrl(params: GetCloneUrlParams): Promise<CloneUrl>;
|
|
27
28
|
};
|
|
28
29
|
commits: {
|
|
@@ -92,4 +93,4 @@ declare class RepoError extends Error {
|
|
|
92
93
|
constructor(message: string, options: RepoErrorOptions);
|
|
93
94
|
}
|
|
94
95
|
//#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 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 };
|
|
96
|
+
export { type Archive, type ArchiveFormat, type AuthenticatedUser, type Branch, type CloneCredentials, type CloneUrl, type Commit, type CreateClientOptions, type CreateWebhookParams, type DeleteWebhookParams, type DetectWebhookProviderInput, type DownloadArchiveParams, type GetAuthenticatedUserParams, type GetBranchParams, type GetCloneCredentialsParams, 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
|
@@ -125,6 +125,10 @@ function createClient(options) {
|
|
|
125
125
|
format
|
|
126
126
|
}));
|
|
127
127
|
},
|
|
128
|
+
getCloneCredentials: async (params) => {
|
|
129
|
+
requireNonEmpty(params.repo, "repo");
|
|
130
|
+
return withRetry(() => provider.getCloneCredentials(params));
|
|
131
|
+
},
|
|
128
132
|
getCloneUrl: async (params) => {
|
|
129
133
|
requireNonEmpty(params.repo, "repo");
|
|
130
134
|
return withRetry(() => provider.getCloneUrl(params));
|
|
@@ -70,4 +70,54 @@ function assertSameOriginUrl(provider, baseUrl, url) {
|
|
|
70
70
|
return url;
|
|
71
71
|
}
|
|
72
72
|
//#endregion
|
|
73
|
-
|
|
73
|
+
//#region src/providers/shared.ts
|
|
74
|
+
function isRecord(value) {
|
|
75
|
+
return typeof value === "object" && value !== null;
|
|
76
|
+
}
|
|
77
|
+
function clampPerPage(limit, max = 100) {
|
|
78
|
+
if (limit === void 0) return void 0;
|
|
79
|
+
return Math.min(Math.max(1, Math.trunc(limit)), max);
|
|
80
|
+
}
|
|
81
|
+
function parseLinkNext(header) {
|
|
82
|
+
if (!header) return void 0;
|
|
83
|
+
for (const part of header.split(",")) {
|
|
84
|
+
const match = part.match(/<([^>]+)>\s*;\s*rel="([^"]+)"/);
|
|
85
|
+
if (match && match[2] === "next") return match[1];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** Percent-encodes a ref path while keeping its `/` separators intact. */
|
|
89
|
+
function encodeRefPath(ref) {
|
|
90
|
+
return ref.split("/").map(encodeURIComponent).join("/");
|
|
91
|
+
}
|
|
92
|
+
function commitWebUrlBuilder(pathSegment) {
|
|
93
|
+
return function commitWebUrl(repoWebUrl, sha) {
|
|
94
|
+
return `${repoWebUrl.replace(/\/+$/, "")}/${pathSegment}/${sha}`;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function filenameFromContentDisposition(header) {
|
|
98
|
+
if (!header) return void 0;
|
|
99
|
+
const extended = header.match(/filename\*=(?:UTF-8'')?([^;]+)/i);
|
|
100
|
+
if (extended?.[1]) return decodeURIComponent(extended[1].trim().replace(/^"|"$/g, ""));
|
|
101
|
+
return header.match(/filename="?([^";]+)"?/i)?.[1]?.trim();
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Embeds clone credentials into their credential-free URL as percent-encoded
|
|
105
|
+
* userinfo — the single place every provider's tokenized clone URL is built.
|
|
106
|
+
* A `null` password yields a bare `user@host` (Gitea puts the token in the
|
|
107
|
+
* username slot); anonymous credentials return the URL untouched.
|
|
108
|
+
*/
|
|
109
|
+
function toCloneUrl(credentials) {
|
|
110
|
+
const { expiresAt, password, url, username } = credentials;
|
|
111
|
+
if (username === null && password === null) return {
|
|
112
|
+
url,
|
|
113
|
+
expiresAt
|
|
114
|
+
};
|
|
115
|
+
const user = username === null ? "" : encodeURIComponent(username);
|
|
116
|
+
const userinfo = password === null ? user : `${user}:${encodeURIComponent(password)}`;
|
|
117
|
+
return {
|
|
118
|
+
url: url.replace("://", `://${userinfo}@`),
|
|
119
|
+
expiresAt
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
export { isRecord as a, assertSameOriginUrl as c, bytesToBase64Url as d, stringToBase64 as f, filenameFromContentDisposition as i, decodeCursor as l, commitWebUrlBuilder as n, parseLinkNext as o, encodeRefPath as r, toCloneUrl as s, clampPerPage as t, encodeCursor as u };
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { D as Namespace, G as Webhook, I as RepoCapabilities, K as WebhookEventType, L as RepoProvider, O as NamespaceKind, R as Repository, V as Tag, i as Branch, j as ProviderName, s as Commit } from "./types-E47xhSNA.js";
|
|
2
2
|
//#region src/providers/testing/index.d.ts
|
|
3
3
|
interface InMemoryNamespaceSeed {
|
|
4
4
|
id: string;
|
package/dist/testing.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as RepoError } from "./errors-Cxq_CqYv.js";
|
|
2
|
-
import {
|
|
2
|
+
import { l as decodeCursor, s as toCloneUrl, u as encodeCursor } from "./shared-C6m-C4My.js";
|
|
3
3
|
//#region src/providers/testing/index.ts
|
|
4
4
|
/**
|
|
5
5
|
* Small on purpose so that consumers exercise cursor-based pagination.
|
|
@@ -149,6 +149,14 @@ function createInMemoryProvider(seed = {}, options = {}) {
|
|
|
149
149
|
if (!repository) throw notFound(`Unknown repository: ${repo}`);
|
|
150
150
|
return repository;
|
|
151
151
|
}
|
|
152
|
+
function getCloneCredentials(params) {
|
|
153
|
+
requireRepository(params.repo);
|
|
154
|
+
return Promise.resolve({
|
|
155
|
+
password: "test",
|
|
156
|
+
url: `https://in-memory.invalid/${params.repo}.git`,
|
|
157
|
+
username: "x-token"
|
|
158
|
+
});
|
|
159
|
+
}
|
|
152
160
|
return {
|
|
153
161
|
name,
|
|
154
162
|
capabilities,
|
|
@@ -262,9 +270,9 @@ function createInMemoryProvider(seed = {}, options = {}) {
|
|
|
262
270
|
filename: `${repository.name}-${params.ref}.${extension}`
|
|
263
271
|
});
|
|
264
272
|
},
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return
|
|
273
|
+
getCloneCredentials,
|
|
274
|
+
async getCloneUrl(params) {
|
|
275
|
+
return toCloneUrl(await getCloneCredentials(params));
|
|
268
276
|
},
|
|
269
277
|
createWebhook(params) {
|
|
270
278
|
requireRepository(params.repo);
|
|
@@ -45,6 +45,20 @@ interface CloneUrl {
|
|
|
45
45
|
url: string;
|
|
46
46
|
expiresAt?: Date;
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Clone credentials kept separate from the URL, for handing to a git credential
|
|
50
|
+
* helper instead of persisting a tokenized remote in `.git/config`. Both
|
|
51
|
+
* components are raw, never percent-encoded.
|
|
52
|
+
*/
|
|
53
|
+
interface CloneCredentials {
|
|
54
|
+
expiresAt?: Date;
|
|
55
|
+
/** The secret, or `null` when the provider carries the token in `username` (Gitea) or access is anonymous. */
|
|
56
|
+
password: string | null;
|
|
57
|
+
/** Clone URL without any embedded credential. */
|
|
58
|
+
url: string;
|
|
59
|
+
/** The static username the provider expects, the token itself for Gitea, or `null` for anonymous access. */
|
|
60
|
+
username: string | null;
|
|
61
|
+
}
|
|
48
62
|
/** The account behind the configured credentials (see the `userProfile` capability). */
|
|
49
63
|
interface AuthenticatedUser {
|
|
50
64
|
id: string;
|
|
@@ -215,6 +229,9 @@ interface DownloadArchiveParams extends BaseParams {
|
|
|
215
229
|
ref: string;
|
|
216
230
|
format?: ArchiveFormat;
|
|
217
231
|
}
|
|
232
|
+
interface GetCloneCredentialsParams extends BaseParams {
|
|
233
|
+
repo: string;
|
|
234
|
+
}
|
|
218
235
|
interface GetCloneUrlParams extends BaseParams {
|
|
219
236
|
repo: string;
|
|
220
237
|
}
|
|
@@ -276,6 +293,8 @@ interface RepoProvider {
|
|
|
276
293
|
/** Prefix-match refs of the requested types, branches before tags, at most `limit` results. */
|
|
277
294
|
searchRefs(params: ProviderSearchRefsParams): Promise<ProviderRefMatch[]>;
|
|
278
295
|
downloadArchive(params: DownloadArchiveParams): Promise<Archive>;
|
|
296
|
+
getCloneCredentials(params: GetCloneCredentialsParams): Promise<CloneCredentials>;
|
|
297
|
+
/** The same credentials embedded in the URL; derived from `getCloneCredentials`. */
|
|
279
298
|
getCloneUrl(params: GetCloneUrlParams): Promise<CloneUrl>;
|
|
280
299
|
createWebhook(params: CreateWebhookParams): Promise<Webhook>;
|
|
281
300
|
listWebhooks(params: ListWebhooksParams): Promise<Page<Webhook>>;
|
|
@@ -284,4 +303,4 @@ interface RepoProvider {
|
|
|
284
303
|
deleteWebhook(params: DeleteWebhookParams): Promise<void>;
|
|
285
304
|
}
|
|
286
305
|
//#endregion
|
|
287
|
-
export {
|
|
306
|
+
export { ParsedWebhookEvent as A, SearchRefsParams as B, ListNamespacesParams as C, Namespace as D, ListWebhooksParams as E, RefType as F, Webhook as G, TokenProvider as H, RepoCapabilities as I, WebhookEventType as K, RepoProvider as L, ProviderRefMatch as M, ProviderSearchRefsParams as N, NamespaceKind as O, RefMatch as P, Repository as R, ListCommitsParams as S, ListTagsParams as T, UpdateWebhookParams as U, Tag as V, UserRef as W, GetTagParams as _, CloneCredentials as a, IncomingWebhookRequest as b, CreateWebhookParams as c, GetAuthenticatedUserParams as d, GetBranchParams as f, GetRepositoryParams as g, GetCommitParams as h, Branch as i, ProviderName as j, Page as k, DeleteWebhookParams as l, GetCloneUrlParams as m, ArchiveFormat as n, CloneUrl as o, GetCloneCredentialsParams as p, WebhookVerificationMethod as q, AuthenticatedUser as r, Commit as s, Archive as t, DownloadArchiveParams as u, GetWebhookParams as v, ListRepositoriesParams as w, ListBranchesParams as x, GitActor as y, ResolveRefParams as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "repo-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
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>",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"version-control",
|
|
23
23
|
"webhooks"
|
|
24
24
|
],
|
|
25
|
-
"homepage": "https://
|
|
25
|
+
"homepage": "https://repo-sdk.dev",
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
28
28
|
"url": "https://github.com/capawesome-team/repo-sdk"
|