@pierre/storage 0.9.3 → 1.0.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 +77 -43
- package/dist/index.cjs +147 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -7
- package/dist/index.d.ts +12 -7
- package/dist/index.js +147 -42
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/src/commit-pack.ts +103 -99
- package/src/commit.ts +373 -365
- package/src/diff-commit.ts +272 -259
- package/src/errors.ts +34 -34
- package/src/fetch.ts +146 -141
- package/src/index.ts +1400 -1249
- package/src/schemas.ts +120 -114
- package/src/stream-utils.ts +225 -208
- package/src/types.ts +378 -354
- package/src/util.ts +41 -34
- package/src/version.ts +1 -1
- package/src/webhook.ts +244 -239
package/README.md
CHANGED
|
@@ -6,9 +6,10 @@ Pierre Git Storage SDK for TypeScript/JavaScript applications.
|
|
|
6
6
|
|
|
7
7
|
- `node packages/git-storage-sdk/tests/full-workflow.js -e production -s pierre -k /home/ian/pierre-prod-key.pem`
|
|
8
8
|
Drives
|
|
9
|
-
the Pierre workflow via the SDK: creates a repository, writes commits, fetches
|
|
10
|
-
data, and confirms storage APIs. Swap in your own private key
|
|
11
|
-
workstation and adjust `-e`/`-s` for
|
|
9
|
+
the Pierre workflow via the SDK: creates a repository, writes commits, fetches
|
|
10
|
+
branch and diff data, and confirms storage APIs. Swap in your own private key
|
|
11
|
+
path when running outside this workstation and adjust `-e`/`-s` for
|
|
12
|
+
non-production environments.
|
|
12
13
|
|
|
13
14
|
## Installation
|
|
14
15
|
|
|
@@ -99,8 +100,8 @@ const readOnlyUrl = await repo.getRemoteURL({
|
|
|
99
100
|
|
|
100
101
|
#### Ephemeral Branches
|
|
101
102
|
|
|
102
|
-
For working with ephemeral branches (temporary branches isolated from the main
|
|
103
|
-
`getEphemeralRemote()`:
|
|
103
|
+
For working with ephemeral branches (temporary branches isolated from the main
|
|
104
|
+
repository), use `getEphemeralRemote()`:
|
|
104
105
|
|
|
105
106
|
```typescript
|
|
106
107
|
// Get ephemeral namespace remote URL
|
|
@@ -109,7 +110,9 @@ const ephemeralUrl = await repo.getEphemeralRemoteURL();
|
|
|
109
110
|
|
|
110
111
|
// Configure separate remotes for default and ephemeral branches
|
|
111
112
|
console.log(`Run: git remote add origin ${await repo.getRemoteURL()}`);
|
|
112
|
-
console.log(
|
|
113
|
+
console.log(
|
|
114
|
+
`Run: git remote add ephemeral ${await repo.getEphemeralRemoteURL()}`
|
|
115
|
+
);
|
|
113
116
|
|
|
114
117
|
// Push ephemeral branch
|
|
115
118
|
// git push ephemeral feature-branch
|
|
@@ -138,6 +141,16 @@ const resp = await repo.getFileStream({
|
|
|
138
141
|
const text = await resp.text();
|
|
139
142
|
console.log(text);
|
|
140
143
|
|
|
144
|
+
// Download repository archive (streaming tar.gz)
|
|
145
|
+
const archiveResp = await repo.getArchiveStream({
|
|
146
|
+
ref: 'main',
|
|
147
|
+
includeGlobs: ['README.md'],
|
|
148
|
+
excludeGlobs: ['vendor/**'],
|
|
149
|
+
archivePrefix: 'repo/',
|
|
150
|
+
});
|
|
151
|
+
const archiveBytes = new Uint8Array(await archiveResp.arrayBuffer());
|
|
152
|
+
console.log(archiveBytes.length);
|
|
153
|
+
|
|
141
154
|
// List all files in the repository
|
|
142
155
|
const files = await repo.listFiles({
|
|
143
156
|
ref: 'main', // optional, defaults to default branch
|
|
@@ -224,10 +237,11 @@ console.log(result.refUpdate.oldSha); // All zeroes when the ref is created
|
|
|
224
237
|
|
|
225
238
|
The builder exposes:
|
|
226
239
|
|
|
227
|
-
- `addFile(path, source, options)` to attach bytes from strings, typed arrays,
|
|
228
|
-
or `File` objects, `ReadableStream`s, or
|
|
229
|
-
-
|
|
230
|
-
|
|
240
|
+
- `addFile(path, source, options)` to attach bytes from strings, typed arrays,
|
|
241
|
+
ArrayBuffers, `Blob` or `File` objects, `ReadableStream`s, or
|
|
242
|
+
iterable/async-iterable sources.
|
|
243
|
+
- `addFileFromString(path, contents, options)` for text helpers (defaults to
|
|
244
|
+
UTF-8 and accepts any Node.js `BufferEncoding`).
|
|
231
245
|
- `deletePath(path)` to remove files or folders.
|
|
232
246
|
- `send()` to finalize the commit and receive metadata about the new commit.
|
|
233
247
|
|
|
@@ -248,39 +262,46 @@ type CommitResult = {
|
|
|
248
262
|
};
|
|
249
263
|
```
|
|
250
264
|
|
|
251
|
-
If the backend reports a failure (for example, the branch advanced past
|
|
252
|
-
builder throws a `RefUpdateError` containing the status,
|
|
265
|
+
If the backend reports a failure (for example, the branch advanced past
|
|
266
|
+
`expectedHeadSha`) the builder throws a `RefUpdateError` containing the status,
|
|
267
|
+
reason, and ref details.
|
|
253
268
|
|
|
254
269
|
**Options**
|
|
255
270
|
|
|
256
|
-
- `targetBranch` (required): Branch name (for example `main`) that will receive
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
-
|
|
260
|
-
|
|
261
|
-
branch
|
|
271
|
+
- `targetBranch` (required): Branch name (for example `main`) that will receive
|
|
272
|
+
the commit.
|
|
273
|
+
- `expectedHeadSha` (optional): Commit SHA that must match the remote tip; omit
|
|
274
|
+
to fast-forward unconditionally.
|
|
275
|
+
- `baseBranch` (optional): Mirrors the `base_branch` metadata and names an
|
|
276
|
+
existing branch whose tip should seed `targetBranch` if it does not exist.
|
|
277
|
+
Leave `expectedHeadSha` empty when creating a new branch from `baseBranch`;
|
|
278
|
+
when both are provided and the branch already exists, `expectedHeadSha`
|
|
262
279
|
continues to enforce the fast-forward guard.
|
|
263
280
|
- `commitMessage` (required): The commit message.
|
|
264
281
|
- `author` (required): Include `name` and `email` for the commit author.
|
|
265
|
-
- `committer` (optional): Include `name` and `email`. If omitted, the author
|
|
282
|
+
- `committer` (optional): Include `name` and `email`. If omitted, the author
|
|
283
|
+
identity is reused.
|
|
266
284
|
- `signal` (optional): Abort an in-flight upload with `AbortController`.
|
|
267
|
-
- `targetRef` (deprecated, optional): Fully qualified ref (for example
|
|
268
|
-
`targetBranch`, which now accepts plain branch
|
|
285
|
+
- `targetRef` (deprecated, optional): Fully qualified ref (for example
|
|
286
|
+
`refs/heads/main`). Prefer `targetBranch`, which now accepts plain branch
|
|
287
|
+
names.
|
|
269
288
|
|
|
270
|
-
> Files are chunked into 4 MiB segments under the hood, so you can stream large
|
|
271
|
-
> buffering them entirely in memory. File paths are normalized
|
|
289
|
+
> Files are chunked into 4 MiB segments under the hood, so you can stream large
|
|
290
|
+
> assets without buffering them entirely in memory. File paths are normalized
|
|
291
|
+
> relative to the repository root.
|
|
272
292
|
|
|
273
|
-
> The `targetBranch` must already exist on the remote repository unless you
|
|
274
|
-
> the repository has no refs). To seed an empty
|
|
275
|
-
>
|
|
276
|
-
>
|
|
277
|
-
>
|
|
293
|
+
> The `targetBranch` must already exist on the remote repository unless you
|
|
294
|
+
> provide `baseBranch` (or the repository has no refs). To seed an empty
|
|
295
|
+
> repository, point to the default branch and omit `expectedHeadSha`. To create
|
|
296
|
+
> a missing branch within an existing repository, set `baseBranch` to the source
|
|
297
|
+
> branch and omit `expectedHeadSha` so the service clones that tip before
|
|
298
|
+
> applying your changes.
|
|
278
299
|
|
|
279
300
|
### Apply a pre-generated diff
|
|
280
301
|
|
|
281
|
-
If you already have a patch (for example, the output of `git diff --binary`) you
|
|
282
|
-
the gateway with a single call. The SDK handles chunking and
|
|
283
|
-
regular commits:
|
|
302
|
+
If you already have a patch (for example, the output of `git diff --binary`) you
|
|
303
|
+
can stream it to the gateway with a single call. The SDK handles chunking and
|
|
304
|
+
NDJSON streaming just like it does for regular commits:
|
|
284
305
|
|
|
285
306
|
```ts
|
|
286
307
|
const fs = await import('node:fs/promises');
|
|
@@ -299,15 +320,16 @@ console.log(diffResult.commitSha);
|
|
|
299
320
|
console.log(diffResult.refUpdate.newSha);
|
|
300
321
|
```
|
|
301
322
|
|
|
302
|
-
The `diff` field accepts a `string`, `Uint8Array`, `ArrayBuffer`, `Blob`,
|
|
303
|
-
iterable, or async iterable of byte chunks—the same
|
|
304
|
-
|
|
305
|
-
|
|
323
|
+
The `diff` field accepts a `string`, `Uint8Array`, `ArrayBuffer`, `Blob`,
|
|
324
|
+
`File`, `ReadableStream`, iterable, or async iterable of byte chunks—the same
|
|
325
|
+
sources supported by the standard commit builder. `createCommitFromDiff` returns
|
|
326
|
+
a `Promise<CommitResult>` and throws a `RefUpdateError` when the server rejects
|
|
327
|
+
the diff (for example, if the branch tip changed).
|
|
306
328
|
|
|
307
329
|
### Streaming Large Files
|
|
308
330
|
|
|
309
|
-
The commit builder accepts any async iterable of bytes, so you can stream large
|
|
310
|
-
buffering:
|
|
331
|
+
The commit builder accepts any async iterable of bytes, so you can stream large
|
|
332
|
+
assets without buffering:
|
|
311
333
|
|
|
312
334
|
```typescript
|
|
313
335
|
import { createReadStream } from 'node:fs';
|
|
@@ -372,6 +394,7 @@ interface Repo {
|
|
|
372
394
|
getEphemeralRemoteURL(options?: GetRemoteURLOptions): Promise<string>;
|
|
373
395
|
|
|
374
396
|
getFileStream(options: GetFileOptions): Promise<Response>;
|
|
397
|
+
getArchiveStream(options?: ArchiveOptions): Promise<Response>;
|
|
375
398
|
listFiles(options?: ListFilesOptions): Promise<ListFilesResult>;
|
|
376
399
|
listBranches(options?: ListBranchesOptions): Promise<ListBranchesResult>;
|
|
377
400
|
listCommits(options?: ListCommitsOptions): Promise<ListCommitsResult>;
|
|
@@ -397,6 +420,16 @@ interface GetFileOptions {
|
|
|
397
420
|
|
|
398
421
|
// getFileStream() returns a standard Fetch Response for streaming bytes
|
|
399
422
|
|
|
423
|
+
interface ArchiveOptions {
|
|
424
|
+
ref?: string; // Branch, tag, or commit SHA (defaults to default branch)
|
|
425
|
+
includeGlobs?: string[];
|
|
426
|
+
excludeGlobs?: string[];
|
|
427
|
+
archivePrefix?: string;
|
|
428
|
+
ttl?: number;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// getArchiveStream() returns a standard Fetch Response for streaming tar.gz bytes
|
|
432
|
+
|
|
400
433
|
interface ListFilesOptions {
|
|
401
434
|
ref?: string; // Branch, tag, or commit SHA
|
|
402
435
|
ttl?: number;
|
|
@@ -639,14 +672,15 @@ interface RestoreCommitResult {
|
|
|
639
672
|
|
|
640
673
|
## Authentication
|
|
641
674
|
|
|
642
|
-
The SDK uses JWT (JSON Web Tokens) for authentication. When you call
|
|
675
|
+
The SDK uses JWT (JSON Web Tokens) for authentication. When you call
|
|
676
|
+
`getRemoteURL()`, it:
|
|
643
677
|
|
|
644
678
|
1. Creates a JWT with your name, repository ID, and requested permissions
|
|
645
679
|
2. Signs it with your key
|
|
646
680
|
3. Embeds it in the Git remote URL as the password
|
|
647
681
|
|
|
648
|
-
The generated URLs are compatible with standard Git clients and include all
|
|
649
|
-
authentication.
|
|
682
|
+
The generated URLs are compatible with standard Git clients and include all
|
|
683
|
+
necessary authentication.
|
|
650
684
|
|
|
651
685
|
## Error Handling
|
|
652
686
|
|
|
@@ -667,9 +701,9 @@ try {
|
|
|
667
701
|
-
|
|
668
702
|
```
|
|
669
703
|
|
|
670
|
-
- Mutating operations (commit builder, `restoreCommit`) throw `RefUpdateError`
|
|
671
|
-
reports a ref failure. Inspect `error.status`,
|
|
672
|
-
`error.refUpdate` for details.
|
|
704
|
+
- Mutating operations (commit builder, `restoreCommit`) throw `RefUpdateError`
|
|
705
|
+
when the backend reports a ref failure. Inspect `error.status`,
|
|
706
|
+
`error.reason`, `error.message`, and `error.refUpdate` for details.
|
|
673
707
|
|
|
674
708
|
## License
|
|
675
709
|
|
package/dist/index.cjs
CHANGED
|
@@ -165,7 +165,9 @@ var commitPackCommitSchema = zod.z.object({
|
|
|
165
165
|
pack_bytes: zod.z.number(),
|
|
166
166
|
blob_count: zod.z.number()
|
|
167
167
|
});
|
|
168
|
-
var restoreCommitCommitSchema = commitPackCommitSchema.omit({
|
|
168
|
+
var restoreCommitCommitSchema = commitPackCommitSchema.omit({
|
|
169
|
+
blob_count: true
|
|
170
|
+
});
|
|
169
171
|
var refUpdateResultWithOptionalsSchema = zod.z.object({
|
|
170
172
|
branch: zod.z.string().optional(),
|
|
171
173
|
old_sha: zod.z.string().optional(),
|
|
@@ -275,7 +277,11 @@ async function parseCommitPackError(response, fallbackMessage) {
|
|
|
275
277
|
if (typeof result.status === "string" && result.status.trim() !== "") {
|
|
276
278
|
statusLabel = result.status.trim();
|
|
277
279
|
}
|
|
278
|
-
refUpdate = toPartialRefUpdateFields(
|
|
280
|
+
refUpdate = toPartialRefUpdateFields(
|
|
281
|
+
result.branch,
|
|
282
|
+
result.old_sha,
|
|
283
|
+
result.new_sha
|
|
284
|
+
);
|
|
279
285
|
if (typeof result.message === "string" && result.message.trim() !== "") {
|
|
280
286
|
message = result.message.trim();
|
|
281
287
|
}
|
|
@@ -506,7 +512,7 @@ function concatChunks(a, b) {
|
|
|
506
512
|
|
|
507
513
|
// package.json
|
|
508
514
|
var package_default = {
|
|
509
|
-
version: "0.
|
|
515
|
+
version: "1.0.0"};
|
|
510
516
|
|
|
511
517
|
// src/version.ts
|
|
512
518
|
var PACKAGE_NAME = "code-storage-sdk";
|
|
@@ -552,7 +558,9 @@ var CommitBuilderImpl = class {
|
|
|
552
558
|
delete this.options.baseBranch;
|
|
553
559
|
} else {
|
|
554
560
|
if (trimmedBase.startsWith("refs/")) {
|
|
555
|
-
throw new Error(
|
|
561
|
+
throw new Error(
|
|
562
|
+
"createCommit baseBranch must not include refs/ prefix"
|
|
563
|
+
);
|
|
556
564
|
}
|
|
557
565
|
this.options.baseBranch = trimmedBase;
|
|
558
566
|
}
|
|
@@ -699,10 +707,7 @@ var FetchCommitTransport = class {
|
|
|
699
707
|
const response = await fetch(this.url, init);
|
|
700
708
|
if (!response.ok) {
|
|
701
709
|
const fallbackMessage = `createCommit request failed (${response.status} ${response.statusText})`;
|
|
702
|
-
const { statusMessage, statusLabel, refUpdate } = await parseCommitPackError(
|
|
703
|
-
response,
|
|
704
|
-
fallbackMessage
|
|
705
|
-
);
|
|
710
|
+
const { statusMessage, statusLabel, refUpdate } = await parseCommitPackError(response, fallbackMessage);
|
|
706
711
|
throw new RefUpdateError(statusMessage, {
|
|
707
712
|
status: statusLabel,
|
|
708
713
|
message: statusMessage,
|
|
@@ -829,7 +834,9 @@ var DiffCommitExecutor = class {
|
|
|
829
834
|
throw new Error("createCommitFromDiff commitMessage is required");
|
|
830
835
|
}
|
|
831
836
|
if (!trimmedAuthorName || !trimmedAuthorEmail) {
|
|
832
|
-
throw new Error(
|
|
837
|
+
throw new Error(
|
|
838
|
+
"createCommitFromDiff author name and email are required"
|
|
839
|
+
);
|
|
833
840
|
}
|
|
834
841
|
this.options.commitMessage = trimmedMessage;
|
|
835
842
|
this.options.author = {
|
|
@@ -845,7 +852,9 @@ var DiffCommitExecutor = class {
|
|
|
845
852
|
delete this.options.baseBranch;
|
|
846
853
|
} else {
|
|
847
854
|
if (trimmedBase.startsWith("refs/")) {
|
|
848
|
-
throw new Error(
|
|
855
|
+
throw new Error(
|
|
856
|
+
"createCommitFromDiff baseBranch must not include refs/ prefix"
|
|
857
|
+
);
|
|
849
858
|
}
|
|
850
859
|
this.options.baseBranch = trimmedBase;
|
|
851
860
|
}
|
|
@@ -911,7 +920,10 @@ var FetchDiffCommitTransport = class {
|
|
|
911
920
|
this.url = `${trimmedBase}/api/v${config.version}/repos/diff-commit`;
|
|
912
921
|
}
|
|
913
922
|
async send(request) {
|
|
914
|
-
const bodyIterable = buildMessageIterable2(
|
|
923
|
+
const bodyIterable = buildMessageIterable2(
|
|
924
|
+
request.metadata,
|
|
925
|
+
request.diffChunks
|
|
926
|
+
);
|
|
915
927
|
const body = toRequestBody(bodyIterable);
|
|
916
928
|
const init = {
|
|
917
929
|
method: "POST",
|
|
@@ -930,10 +942,7 @@ var FetchDiffCommitTransport = class {
|
|
|
930
942
|
const response = await fetch(this.url, init);
|
|
931
943
|
if (!response.ok) {
|
|
932
944
|
const fallbackMessage = `createCommitFromDiff request failed (${response.status} ${response.statusText})`;
|
|
933
|
-
const { statusMessage, statusLabel, refUpdate } = await parseCommitPackError(
|
|
934
|
-
response,
|
|
935
|
-
fallbackMessage
|
|
936
|
-
);
|
|
945
|
+
const { statusMessage, statusLabel, refUpdate } = await parseCommitPackError(response, fallbackMessage);
|
|
937
946
|
throw new RefUpdateError(statusMessage, {
|
|
938
947
|
status: statusLabel,
|
|
939
948
|
message: statusMessage,
|
|
@@ -975,7 +984,9 @@ function normalizeDiffCommitOptions(options) {
|
|
|
975
984
|
const name = options.committer.name?.trim();
|
|
976
985
|
const email = options.committer.email?.trim();
|
|
977
986
|
if (!name || !email) {
|
|
978
|
-
throw new Error(
|
|
987
|
+
throw new Error(
|
|
988
|
+
"createCommitFromDiff committer name and email are required when provided"
|
|
989
|
+
);
|
|
979
990
|
}
|
|
980
991
|
committer = { name, email };
|
|
981
992
|
}
|
|
@@ -1001,12 +1012,16 @@ function normalizeBranchName2(value) {
|
|
|
1001
1012
|
if (trimmed.startsWith("refs/heads/")) {
|
|
1002
1013
|
const branch = trimmed.slice("refs/heads/".length).trim();
|
|
1003
1014
|
if (!branch) {
|
|
1004
|
-
throw new Error(
|
|
1015
|
+
throw new Error(
|
|
1016
|
+
"createCommitFromDiff targetBranch must include a branch name"
|
|
1017
|
+
);
|
|
1005
1018
|
}
|
|
1006
1019
|
return branch;
|
|
1007
1020
|
}
|
|
1008
1021
|
if (trimmed.startsWith("refs/")) {
|
|
1009
|
-
throw new Error(
|
|
1022
|
+
throw new Error(
|
|
1023
|
+
"createCommitFromDiff targetBranch must not include refs/ prefix"
|
|
1024
|
+
);
|
|
1010
1025
|
}
|
|
1011
1026
|
return trimmed;
|
|
1012
1027
|
}
|
|
@@ -1486,7 +1501,11 @@ function parseRestoreCommitPayload(payload) {
|
|
|
1486
1501
|
failure: {
|
|
1487
1502
|
status: result.status,
|
|
1488
1503
|
message: result.message,
|
|
1489
|
-
refUpdate: toPartialRefUpdate(
|
|
1504
|
+
refUpdate: toPartialRefUpdate(
|
|
1505
|
+
result.branch,
|
|
1506
|
+
result.old_sha,
|
|
1507
|
+
result.new_sha
|
|
1508
|
+
)
|
|
1490
1509
|
}
|
|
1491
1510
|
};
|
|
1492
1511
|
}
|
|
@@ -1504,7 +1523,10 @@ function httpStatusToRestoreStatus(status) {
|
|
|
1504
1523
|
}
|
|
1505
1524
|
function getApiInstance(baseUrl, version) {
|
|
1506
1525
|
if (!apiInstanceMap.has(`${baseUrl}--${version}`)) {
|
|
1507
|
-
apiInstanceMap.set(
|
|
1526
|
+
apiInstanceMap.set(
|
|
1527
|
+
`${baseUrl}--${version}`,
|
|
1528
|
+
new ApiFetcher(baseUrl, version)
|
|
1529
|
+
);
|
|
1508
1530
|
}
|
|
1509
1531
|
return apiInstanceMap.get(`${baseUrl}--${version}`);
|
|
1510
1532
|
}
|
|
@@ -1740,13 +1762,17 @@ var RepoImpl = class {
|
|
|
1740
1762
|
}
|
|
1741
1763
|
api;
|
|
1742
1764
|
async getRemoteURL(urlOptions) {
|
|
1743
|
-
const url = new URL(
|
|
1765
|
+
const url = new URL(
|
|
1766
|
+
`https://${this.options.storageBaseUrl}/${this.id}.git`
|
|
1767
|
+
);
|
|
1744
1768
|
url.username = `t`;
|
|
1745
1769
|
url.password = await this.generateJWT(this.id, urlOptions);
|
|
1746
1770
|
return url.toString();
|
|
1747
1771
|
}
|
|
1748
1772
|
async getEphemeralRemoteURL(urlOptions) {
|
|
1749
|
-
const url = new URL(
|
|
1773
|
+
const url = new URL(
|
|
1774
|
+
`https://${this.options.storageBaseUrl}/${this.id}+ephemeral.git`
|
|
1775
|
+
);
|
|
1750
1776
|
url.username = `t`;
|
|
1751
1777
|
url.password = await this.generateJWT(this.id, urlOptions);
|
|
1752
1778
|
return url.toString();
|
|
@@ -1771,6 +1797,32 @@ var RepoImpl = class {
|
|
|
1771
1797
|
}
|
|
1772
1798
|
return this.api.get({ path: "repos/file", params }, jwt);
|
|
1773
1799
|
}
|
|
1800
|
+
async getArchiveStream(options = {}) {
|
|
1801
|
+
const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
|
|
1802
|
+
const jwt = await this.generateJWT(this.id, {
|
|
1803
|
+
permissions: ["git:read"],
|
|
1804
|
+
ttl
|
|
1805
|
+
});
|
|
1806
|
+
const body = {};
|
|
1807
|
+
const ref = options.ref?.trim();
|
|
1808
|
+
if (ref) {
|
|
1809
|
+
body.ref = ref;
|
|
1810
|
+
}
|
|
1811
|
+
if (Array.isArray(options.includeGlobs) && options.includeGlobs.length > 0) {
|
|
1812
|
+
body.include_globs = options.includeGlobs;
|
|
1813
|
+
}
|
|
1814
|
+
if (Array.isArray(options.excludeGlobs) && options.excludeGlobs.length > 0) {
|
|
1815
|
+
body.exclude_globs = options.excludeGlobs;
|
|
1816
|
+
}
|
|
1817
|
+
if (typeof options.archivePrefix === "string") {
|
|
1818
|
+
const prefix = options.archivePrefix.trim();
|
|
1819
|
+
if (prefix) {
|
|
1820
|
+
body.archive = { prefix };
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
const path = Object.keys(body).length > 0 ? { path: "repos/archive", body } : "repos/archive";
|
|
1824
|
+
return this.api.post(path, jwt);
|
|
1825
|
+
}
|
|
1774
1826
|
async listFiles(options) {
|
|
1775
1827
|
const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
|
|
1776
1828
|
const jwt = await this.generateJWT(this.id, {
|
|
@@ -1785,7 +1837,10 @@ var RepoImpl = class {
|
|
|
1785
1837
|
params.ephemeral = String(options.ephemeral);
|
|
1786
1838
|
}
|
|
1787
1839
|
const response = await this.api.get(
|
|
1788
|
-
{
|
|
1840
|
+
{
|
|
1841
|
+
path: "repos/files",
|
|
1842
|
+
params: Object.keys(params).length ? params : void 0
|
|
1843
|
+
},
|
|
1789
1844
|
jwt
|
|
1790
1845
|
);
|
|
1791
1846
|
const raw = listFilesResponseSchema.parse(await response.json());
|
|
@@ -1809,7 +1864,10 @@ var RepoImpl = class {
|
|
|
1809
1864
|
params.limit = limit.toString();
|
|
1810
1865
|
}
|
|
1811
1866
|
}
|
|
1812
|
-
const response = await this.api.get(
|
|
1867
|
+
const response = await this.api.get(
|
|
1868
|
+
{ path: "repos/branches", params },
|
|
1869
|
+
jwt
|
|
1870
|
+
);
|
|
1813
1871
|
const raw = listBranchesResponseSchema.parse(await response.json());
|
|
1814
1872
|
return transformListBranchesResult({
|
|
1815
1873
|
...raw,
|
|
@@ -1852,7 +1910,10 @@ var RepoImpl = class {
|
|
|
1852
1910
|
permissions: ["git:read"],
|
|
1853
1911
|
ttl
|
|
1854
1912
|
});
|
|
1855
|
-
const response = await this.api.get(
|
|
1913
|
+
const response = await this.api.get(
|
|
1914
|
+
{ path: "repos/notes", params: { sha } },
|
|
1915
|
+
jwt
|
|
1916
|
+
);
|
|
1856
1917
|
const raw = noteReadResponseSchema.parse(await response.json());
|
|
1857
1918
|
return transformNoteReadResult(raw);
|
|
1858
1919
|
}
|
|
@@ -1884,7 +1945,11 @@ var RepoImpl = class {
|
|
|
1884
1945
|
{
|
|
1885
1946
|
status: result.result.status,
|
|
1886
1947
|
message: result.result.message,
|
|
1887
|
-
refUpdate: toPartialRefUpdate(
|
|
1948
|
+
refUpdate: toPartialRefUpdate(
|
|
1949
|
+
result.targetRef,
|
|
1950
|
+
result.baseCommit,
|
|
1951
|
+
result.newRefSha
|
|
1952
|
+
)
|
|
1888
1953
|
}
|
|
1889
1954
|
);
|
|
1890
1955
|
}
|
|
@@ -1918,7 +1983,11 @@ var RepoImpl = class {
|
|
|
1918
1983
|
{
|
|
1919
1984
|
status: result.result.status,
|
|
1920
1985
|
message: result.result.message,
|
|
1921
|
-
refUpdate: toPartialRefUpdate(
|
|
1986
|
+
refUpdate: toPartialRefUpdate(
|
|
1987
|
+
result.targetRef,
|
|
1988
|
+
result.baseCommit,
|
|
1989
|
+
result.newRefSha
|
|
1990
|
+
)
|
|
1922
1991
|
}
|
|
1923
1992
|
);
|
|
1924
1993
|
}
|
|
@@ -1945,7 +2014,9 @@ var RepoImpl = class {
|
|
|
1945
2014
|
const authorName = options.author.name?.trim();
|
|
1946
2015
|
const authorEmail = options.author.email?.trim();
|
|
1947
2016
|
if (!authorName || !authorEmail) {
|
|
1948
|
-
throw new Error(
|
|
2017
|
+
throw new Error(
|
|
2018
|
+
"deleteNote author name and email are required when provided"
|
|
2019
|
+
);
|
|
1949
2020
|
}
|
|
1950
2021
|
body.author = {
|
|
1951
2022
|
name: authorName,
|
|
@@ -1962,7 +2033,11 @@ var RepoImpl = class {
|
|
|
1962
2033
|
{
|
|
1963
2034
|
status: result.result.status,
|
|
1964
2035
|
message: result.result.message,
|
|
1965
|
-
refUpdate: toPartialRefUpdate(
|
|
2036
|
+
refUpdate: toPartialRefUpdate(
|
|
2037
|
+
result.targetRef,
|
|
2038
|
+
result.baseCommit,
|
|
2039
|
+
result.newRefSha
|
|
2040
|
+
)
|
|
1966
2041
|
}
|
|
1967
2042
|
);
|
|
1968
2043
|
}
|
|
@@ -1989,7 +2064,10 @@ var RepoImpl = class {
|
|
|
1989
2064
|
if (options.paths && options.paths.length > 0) {
|
|
1990
2065
|
params.path = options.paths;
|
|
1991
2066
|
}
|
|
1992
|
-
const response = await this.api.get(
|
|
2067
|
+
const response = await this.api.get(
|
|
2068
|
+
{ path: "repos/branches/diff", params },
|
|
2069
|
+
jwt
|
|
2070
|
+
);
|
|
1993
2071
|
const raw = branchDiffResponseSchema.parse(await response.json());
|
|
1994
2072
|
return transformBranchDiffResult(raw);
|
|
1995
2073
|
}
|
|
@@ -2028,8 +2106,9 @@ var RepoImpl = class {
|
|
|
2028
2106
|
...typeof options.query.caseSensitive === "boolean" ? { case_sensitive: options.query.caseSensitive } : {}
|
|
2029
2107
|
}
|
|
2030
2108
|
};
|
|
2031
|
-
|
|
2032
|
-
|
|
2109
|
+
const ref = options.ref?.trim() || options.rev?.trim();
|
|
2110
|
+
if (ref) {
|
|
2111
|
+
body.ref = ref;
|
|
2033
2112
|
}
|
|
2034
2113
|
if (Array.isArray(options.paths) && options.paths.length > 0) {
|
|
2035
2114
|
body.paths = options.paths;
|
|
@@ -2085,9 +2164,14 @@ var RepoImpl = class {
|
|
|
2085
2164
|
if (options.ref) {
|
|
2086
2165
|
body.ref = options.ref;
|
|
2087
2166
|
}
|
|
2088
|
-
const response = await this.api.post(
|
|
2167
|
+
const response = await this.api.post(
|
|
2168
|
+
{ path: "repos/pull-upstream", body },
|
|
2169
|
+
jwt
|
|
2170
|
+
);
|
|
2089
2171
|
if (response.status !== 202) {
|
|
2090
|
-
throw new Error(
|
|
2172
|
+
throw new Error(
|
|
2173
|
+
`Pull Upstream failed: ${response.status} ${await response.text()}`
|
|
2174
|
+
);
|
|
2091
2175
|
}
|
|
2092
2176
|
return;
|
|
2093
2177
|
}
|
|
@@ -2115,7 +2199,10 @@ var RepoImpl = class {
|
|
|
2115
2199
|
if (options.targetIsEphemeral === true) {
|
|
2116
2200
|
body.target_is_ephemeral = true;
|
|
2117
2201
|
}
|
|
2118
|
-
const response = await this.api.post(
|
|
2202
|
+
const response = await this.api.post(
|
|
2203
|
+
{ path: "repos/branches/create", body },
|
|
2204
|
+
jwt
|
|
2205
|
+
);
|
|
2119
2206
|
const raw = createBranchResponseSchema.parse(await response.json());
|
|
2120
2207
|
return transformCreateBranchResult(raw);
|
|
2121
2208
|
}
|
|
@@ -2125,7 +2212,9 @@ var RepoImpl = class {
|
|
|
2125
2212
|
throw new Error("restoreCommit targetBranch is required");
|
|
2126
2213
|
}
|
|
2127
2214
|
if (targetBranch.startsWith("refs/")) {
|
|
2128
|
-
throw new Error(
|
|
2215
|
+
throw new Error(
|
|
2216
|
+
"restoreCommit targetBranch must not include refs/ prefix"
|
|
2217
|
+
);
|
|
2129
2218
|
}
|
|
2130
2219
|
const targetCommitSha = options?.targetCommitSha?.trim();
|
|
2131
2220
|
if (!targetCommitSha) {
|
|
@@ -2161,7 +2250,9 @@ var RepoImpl = class {
|
|
|
2161
2250
|
const committerName = options.committer.name?.trim();
|
|
2162
2251
|
const committerEmail = options.committer.email?.trim();
|
|
2163
2252
|
if (!committerName || !committerEmail) {
|
|
2164
|
-
throw new Error(
|
|
2253
|
+
throw new Error(
|
|
2254
|
+
"restoreCommit committer name and email are required when provided"
|
|
2255
|
+
);
|
|
2165
2256
|
}
|
|
2166
2257
|
metadata.committer = {
|
|
2167
2258
|
name: committerName,
|
|
@@ -2311,10 +2402,14 @@ var GitStorage = class _GitStorage {
|
|
|
2311
2402
|
path: "repos",
|
|
2312
2403
|
body: {
|
|
2313
2404
|
...baseRepoOptions && { base_repo: baseRepoOptions },
|
|
2314
|
-
...resolvedDefaultBranch && {
|
|
2405
|
+
...resolvedDefaultBranch && {
|
|
2406
|
+
default_branch: resolvedDefaultBranch
|
|
2407
|
+
}
|
|
2315
2408
|
}
|
|
2316
2409
|
} : "repos";
|
|
2317
|
-
const resp = await this.api.post(createRepoPath, jwt, {
|
|
2410
|
+
const resp = await this.api.post(createRepoPath, jwt, {
|
|
2411
|
+
allowedStatus: [409]
|
|
2412
|
+
});
|
|
2318
2413
|
if (resp.status === 409) {
|
|
2319
2414
|
throw new Error("Repository already exists");
|
|
2320
2415
|
}
|
|
@@ -2368,7 +2463,12 @@ var GitStorage = class _GitStorage {
|
|
|
2368
2463
|
}
|
|
2369
2464
|
const body = await resp.json();
|
|
2370
2465
|
const defaultBranch = body.default_branch ?? "main";
|
|
2371
|
-
return new RepoImpl(
|
|
2466
|
+
return new RepoImpl(
|
|
2467
|
+
options.id,
|
|
2468
|
+
defaultBranch,
|
|
2469
|
+
this.options,
|
|
2470
|
+
this.generateJWT.bind(this)
|
|
2471
|
+
);
|
|
2372
2472
|
}
|
|
2373
2473
|
/**
|
|
2374
2474
|
* Delete a repository by ID
|
|
@@ -2381,7 +2481,9 @@ var GitStorage = class _GitStorage {
|
|
|
2381
2481
|
permissions: ["repo:write"],
|
|
2382
2482
|
ttl
|
|
2383
2483
|
});
|
|
2384
|
-
const resp = await this.api.delete("repos/delete", jwt, {
|
|
2484
|
+
const resp = await this.api.delete("repos/delete", jwt, {
|
|
2485
|
+
allowedStatus: [404, 409]
|
|
2486
|
+
});
|
|
2385
2487
|
if (resp.status === 404) {
|
|
2386
2488
|
throw new Error("Repository not found");
|
|
2387
2489
|
}
|
|
@@ -2407,7 +2509,10 @@ var GitStorage = class _GitStorage {
|
|
|
2407
2509
|
*/
|
|
2408
2510
|
async generateJWT(repoId, options) {
|
|
2409
2511
|
const permissions = options?.permissions || ["git:write", "git:read"];
|
|
2410
|
-
const ttl = resolveInvocationTtlSeconds(
|
|
2512
|
+
const ttl = resolveInvocationTtlSeconds(
|
|
2513
|
+
options,
|
|
2514
|
+
this.options.defaultTTL ?? 365 * 24 * 60 * 60
|
|
2515
|
+
);
|
|
2411
2516
|
const now = Math.floor(Date.now() / 1e3);
|
|
2412
2517
|
const payload = {
|
|
2413
2518
|
iss: this.options.name,
|