@pierre/storage 0.9.3 → 1.0.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/README.md +77 -43
- package/dist/index.cjs +152 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -7
- package/dist/index.d.ts +13 -7
- package/dist/index.js +152 -43
- 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 +1404 -1249
- package/src/schemas.ts +120 -114
- package/src/stream-utils.ts +225 -208
- package/src/types.ts +379 -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.1"};
|
|
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
|
}
|
|
@@ -1728,9 +1750,10 @@ async function parseNoteWriteResponse(response, method) {
|
|
|
1728
1750
|
});
|
|
1729
1751
|
}
|
|
1730
1752
|
var RepoImpl = class {
|
|
1731
|
-
constructor(id, defaultBranch, options, generateJWT) {
|
|
1753
|
+
constructor(id, defaultBranch, createdAt, options, generateJWT) {
|
|
1732
1754
|
this.id = id;
|
|
1733
1755
|
this.defaultBranch = defaultBranch;
|
|
1756
|
+
this.createdAt = createdAt;
|
|
1734
1757
|
this.options = options;
|
|
1735
1758
|
this.generateJWT = generateJWT;
|
|
1736
1759
|
this.api = getApiInstance(
|
|
@@ -1740,13 +1763,17 @@ var RepoImpl = class {
|
|
|
1740
1763
|
}
|
|
1741
1764
|
api;
|
|
1742
1765
|
async getRemoteURL(urlOptions) {
|
|
1743
|
-
const url = new URL(
|
|
1766
|
+
const url = new URL(
|
|
1767
|
+
`https://${this.options.storageBaseUrl}/${this.id}.git`
|
|
1768
|
+
);
|
|
1744
1769
|
url.username = `t`;
|
|
1745
1770
|
url.password = await this.generateJWT(this.id, urlOptions);
|
|
1746
1771
|
return url.toString();
|
|
1747
1772
|
}
|
|
1748
1773
|
async getEphemeralRemoteURL(urlOptions) {
|
|
1749
|
-
const url = new URL(
|
|
1774
|
+
const url = new URL(
|
|
1775
|
+
`https://${this.options.storageBaseUrl}/${this.id}+ephemeral.git`
|
|
1776
|
+
);
|
|
1750
1777
|
url.username = `t`;
|
|
1751
1778
|
url.password = await this.generateJWT(this.id, urlOptions);
|
|
1752
1779
|
return url.toString();
|
|
@@ -1771,6 +1798,32 @@ var RepoImpl = class {
|
|
|
1771
1798
|
}
|
|
1772
1799
|
return this.api.get({ path: "repos/file", params }, jwt);
|
|
1773
1800
|
}
|
|
1801
|
+
async getArchiveStream(options = {}) {
|
|
1802
|
+
const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
|
|
1803
|
+
const jwt = await this.generateJWT(this.id, {
|
|
1804
|
+
permissions: ["git:read"],
|
|
1805
|
+
ttl
|
|
1806
|
+
});
|
|
1807
|
+
const body = {};
|
|
1808
|
+
const ref = options.ref?.trim();
|
|
1809
|
+
if (ref) {
|
|
1810
|
+
body.ref = ref;
|
|
1811
|
+
}
|
|
1812
|
+
if (Array.isArray(options.includeGlobs) && options.includeGlobs.length > 0) {
|
|
1813
|
+
body.include_globs = options.includeGlobs;
|
|
1814
|
+
}
|
|
1815
|
+
if (Array.isArray(options.excludeGlobs) && options.excludeGlobs.length > 0) {
|
|
1816
|
+
body.exclude_globs = options.excludeGlobs;
|
|
1817
|
+
}
|
|
1818
|
+
if (typeof options.archivePrefix === "string") {
|
|
1819
|
+
const prefix = options.archivePrefix.trim();
|
|
1820
|
+
if (prefix) {
|
|
1821
|
+
body.archive = { prefix };
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
const path = Object.keys(body).length > 0 ? { path: "repos/archive", body } : "repos/archive";
|
|
1825
|
+
return this.api.post(path, jwt);
|
|
1826
|
+
}
|
|
1774
1827
|
async listFiles(options) {
|
|
1775
1828
|
const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
|
|
1776
1829
|
const jwt = await this.generateJWT(this.id, {
|
|
@@ -1785,7 +1838,10 @@ var RepoImpl = class {
|
|
|
1785
1838
|
params.ephemeral = String(options.ephemeral);
|
|
1786
1839
|
}
|
|
1787
1840
|
const response = await this.api.get(
|
|
1788
|
-
{
|
|
1841
|
+
{
|
|
1842
|
+
path: "repos/files",
|
|
1843
|
+
params: Object.keys(params).length ? params : void 0
|
|
1844
|
+
},
|
|
1789
1845
|
jwt
|
|
1790
1846
|
);
|
|
1791
1847
|
const raw = listFilesResponseSchema.parse(await response.json());
|
|
@@ -1809,7 +1865,10 @@ var RepoImpl = class {
|
|
|
1809
1865
|
params.limit = limit.toString();
|
|
1810
1866
|
}
|
|
1811
1867
|
}
|
|
1812
|
-
const response = await this.api.get(
|
|
1868
|
+
const response = await this.api.get(
|
|
1869
|
+
{ path: "repos/branches", params },
|
|
1870
|
+
jwt
|
|
1871
|
+
);
|
|
1813
1872
|
const raw = listBranchesResponseSchema.parse(await response.json());
|
|
1814
1873
|
return transformListBranchesResult({
|
|
1815
1874
|
...raw,
|
|
@@ -1852,7 +1911,10 @@ var RepoImpl = class {
|
|
|
1852
1911
|
permissions: ["git:read"],
|
|
1853
1912
|
ttl
|
|
1854
1913
|
});
|
|
1855
|
-
const response = await this.api.get(
|
|
1914
|
+
const response = await this.api.get(
|
|
1915
|
+
{ path: "repos/notes", params: { sha } },
|
|
1916
|
+
jwt
|
|
1917
|
+
);
|
|
1856
1918
|
const raw = noteReadResponseSchema.parse(await response.json());
|
|
1857
1919
|
return transformNoteReadResult(raw);
|
|
1858
1920
|
}
|
|
@@ -1884,7 +1946,11 @@ var RepoImpl = class {
|
|
|
1884
1946
|
{
|
|
1885
1947
|
status: result.result.status,
|
|
1886
1948
|
message: result.result.message,
|
|
1887
|
-
refUpdate: toPartialRefUpdate(
|
|
1949
|
+
refUpdate: toPartialRefUpdate(
|
|
1950
|
+
result.targetRef,
|
|
1951
|
+
result.baseCommit,
|
|
1952
|
+
result.newRefSha
|
|
1953
|
+
)
|
|
1888
1954
|
}
|
|
1889
1955
|
);
|
|
1890
1956
|
}
|
|
@@ -1918,7 +1984,11 @@ var RepoImpl = class {
|
|
|
1918
1984
|
{
|
|
1919
1985
|
status: result.result.status,
|
|
1920
1986
|
message: result.result.message,
|
|
1921
|
-
refUpdate: toPartialRefUpdate(
|
|
1987
|
+
refUpdate: toPartialRefUpdate(
|
|
1988
|
+
result.targetRef,
|
|
1989
|
+
result.baseCommit,
|
|
1990
|
+
result.newRefSha
|
|
1991
|
+
)
|
|
1922
1992
|
}
|
|
1923
1993
|
);
|
|
1924
1994
|
}
|
|
@@ -1945,7 +2015,9 @@ var RepoImpl = class {
|
|
|
1945
2015
|
const authorName = options.author.name?.trim();
|
|
1946
2016
|
const authorEmail = options.author.email?.trim();
|
|
1947
2017
|
if (!authorName || !authorEmail) {
|
|
1948
|
-
throw new Error(
|
|
2018
|
+
throw new Error(
|
|
2019
|
+
"deleteNote author name and email are required when provided"
|
|
2020
|
+
);
|
|
1949
2021
|
}
|
|
1950
2022
|
body.author = {
|
|
1951
2023
|
name: authorName,
|
|
@@ -1962,7 +2034,11 @@ var RepoImpl = class {
|
|
|
1962
2034
|
{
|
|
1963
2035
|
status: result.result.status,
|
|
1964
2036
|
message: result.result.message,
|
|
1965
|
-
refUpdate: toPartialRefUpdate(
|
|
2037
|
+
refUpdate: toPartialRefUpdate(
|
|
2038
|
+
result.targetRef,
|
|
2039
|
+
result.baseCommit,
|
|
2040
|
+
result.newRefSha
|
|
2041
|
+
)
|
|
1966
2042
|
}
|
|
1967
2043
|
);
|
|
1968
2044
|
}
|
|
@@ -1989,7 +2065,10 @@ var RepoImpl = class {
|
|
|
1989
2065
|
if (options.paths && options.paths.length > 0) {
|
|
1990
2066
|
params.path = options.paths;
|
|
1991
2067
|
}
|
|
1992
|
-
const response = await this.api.get(
|
|
2068
|
+
const response = await this.api.get(
|
|
2069
|
+
{ path: "repos/branches/diff", params },
|
|
2070
|
+
jwt
|
|
2071
|
+
);
|
|
1993
2072
|
const raw = branchDiffResponseSchema.parse(await response.json());
|
|
1994
2073
|
return transformBranchDiffResult(raw);
|
|
1995
2074
|
}
|
|
@@ -2028,8 +2107,9 @@ var RepoImpl = class {
|
|
|
2028
2107
|
...typeof options.query.caseSensitive === "boolean" ? { case_sensitive: options.query.caseSensitive } : {}
|
|
2029
2108
|
}
|
|
2030
2109
|
};
|
|
2031
|
-
|
|
2032
|
-
|
|
2110
|
+
const ref = options.ref?.trim() || options.rev?.trim();
|
|
2111
|
+
if (ref) {
|
|
2112
|
+
body.ref = ref;
|
|
2033
2113
|
}
|
|
2034
2114
|
if (Array.isArray(options.paths) && options.paths.length > 0) {
|
|
2035
2115
|
body.paths = options.paths;
|
|
@@ -2085,9 +2165,14 @@ var RepoImpl = class {
|
|
|
2085
2165
|
if (options.ref) {
|
|
2086
2166
|
body.ref = options.ref;
|
|
2087
2167
|
}
|
|
2088
|
-
const response = await this.api.post(
|
|
2168
|
+
const response = await this.api.post(
|
|
2169
|
+
{ path: "repos/pull-upstream", body },
|
|
2170
|
+
jwt
|
|
2171
|
+
);
|
|
2089
2172
|
if (response.status !== 202) {
|
|
2090
|
-
throw new Error(
|
|
2173
|
+
throw new Error(
|
|
2174
|
+
`Pull Upstream failed: ${response.status} ${await response.text()}`
|
|
2175
|
+
);
|
|
2091
2176
|
}
|
|
2092
2177
|
return;
|
|
2093
2178
|
}
|
|
@@ -2115,7 +2200,10 @@ var RepoImpl = class {
|
|
|
2115
2200
|
if (options.targetIsEphemeral === true) {
|
|
2116
2201
|
body.target_is_ephemeral = true;
|
|
2117
2202
|
}
|
|
2118
|
-
const response = await this.api.post(
|
|
2203
|
+
const response = await this.api.post(
|
|
2204
|
+
{ path: "repos/branches/create", body },
|
|
2205
|
+
jwt
|
|
2206
|
+
);
|
|
2119
2207
|
const raw = createBranchResponseSchema.parse(await response.json());
|
|
2120
2208
|
return transformCreateBranchResult(raw);
|
|
2121
2209
|
}
|
|
@@ -2125,7 +2213,9 @@ var RepoImpl = class {
|
|
|
2125
2213
|
throw new Error("restoreCommit targetBranch is required");
|
|
2126
2214
|
}
|
|
2127
2215
|
if (targetBranch.startsWith("refs/")) {
|
|
2128
|
-
throw new Error(
|
|
2216
|
+
throw new Error(
|
|
2217
|
+
"restoreCommit targetBranch must not include refs/ prefix"
|
|
2218
|
+
);
|
|
2129
2219
|
}
|
|
2130
2220
|
const targetCommitSha = options?.targetCommitSha?.trim();
|
|
2131
2221
|
if (!targetCommitSha) {
|
|
@@ -2161,7 +2251,9 @@ var RepoImpl = class {
|
|
|
2161
2251
|
const committerName = options.committer.name?.trim();
|
|
2162
2252
|
const committerEmail = options.committer.email?.trim();
|
|
2163
2253
|
if (!committerName || !committerEmail) {
|
|
2164
|
-
throw new Error(
|
|
2254
|
+
throw new Error(
|
|
2255
|
+
"restoreCommit committer name and email are required when provided"
|
|
2256
|
+
);
|
|
2165
2257
|
}
|
|
2166
2258
|
metadata.committer = {
|
|
2167
2259
|
name: committerName,
|
|
@@ -2311,16 +2403,21 @@ var GitStorage = class _GitStorage {
|
|
|
2311
2403
|
path: "repos",
|
|
2312
2404
|
body: {
|
|
2313
2405
|
...baseRepoOptions && { base_repo: baseRepoOptions },
|
|
2314
|
-
...resolvedDefaultBranch && {
|
|
2406
|
+
...resolvedDefaultBranch && {
|
|
2407
|
+
default_branch: resolvedDefaultBranch
|
|
2408
|
+
}
|
|
2315
2409
|
}
|
|
2316
2410
|
} : "repos";
|
|
2317
|
-
const resp = await this.api.post(createRepoPath, jwt, {
|
|
2411
|
+
const resp = await this.api.post(createRepoPath, jwt, {
|
|
2412
|
+
allowedStatus: [409]
|
|
2413
|
+
});
|
|
2318
2414
|
if (resp.status === 409) {
|
|
2319
2415
|
throw new Error("Repository already exists");
|
|
2320
2416
|
}
|
|
2321
2417
|
return new RepoImpl(
|
|
2322
2418
|
repoId,
|
|
2323
2419
|
resolvedDefaultBranch ?? "main",
|
|
2420
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
2324
2421
|
this.options,
|
|
2325
2422
|
this.generateJWT.bind(this)
|
|
2326
2423
|
);
|
|
@@ -2368,7 +2465,14 @@ var GitStorage = class _GitStorage {
|
|
|
2368
2465
|
}
|
|
2369
2466
|
const body = await resp.json();
|
|
2370
2467
|
const defaultBranch = body.default_branch ?? "main";
|
|
2371
|
-
|
|
2468
|
+
const createdAt = body.created_at ?? "";
|
|
2469
|
+
return new RepoImpl(
|
|
2470
|
+
options.id,
|
|
2471
|
+
defaultBranch,
|
|
2472
|
+
createdAt,
|
|
2473
|
+
this.options,
|
|
2474
|
+
this.generateJWT.bind(this)
|
|
2475
|
+
);
|
|
2372
2476
|
}
|
|
2373
2477
|
/**
|
|
2374
2478
|
* Delete a repository by ID
|
|
@@ -2381,7 +2485,9 @@ var GitStorage = class _GitStorage {
|
|
|
2381
2485
|
permissions: ["repo:write"],
|
|
2382
2486
|
ttl
|
|
2383
2487
|
});
|
|
2384
|
-
const resp = await this.api.delete("repos/delete", jwt, {
|
|
2488
|
+
const resp = await this.api.delete("repos/delete", jwt, {
|
|
2489
|
+
allowedStatus: [404, 409]
|
|
2490
|
+
});
|
|
2385
2491
|
if (resp.status === 404) {
|
|
2386
2492
|
throw new Error("Repository not found");
|
|
2387
2493
|
}
|
|
@@ -2407,7 +2513,10 @@ var GitStorage = class _GitStorage {
|
|
|
2407
2513
|
*/
|
|
2408
2514
|
async generateJWT(repoId, options) {
|
|
2409
2515
|
const permissions = options?.permissions || ["git:write", "git:read"];
|
|
2410
|
-
const ttl = resolveInvocationTtlSeconds(
|
|
2516
|
+
const ttl = resolveInvocationTtlSeconds(
|
|
2517
|
+
options,
|
|
2518
|
+
this.options.defaultTTL ?? 365 * 24 * 60 * 60
|
|
2519
|
+
);
|
|
2411
2520
|
const now = Math.floor(Date.now() / 1e3);
|
|
2412
2521
|
const payload = {
|
|
2413
2522
|
iss: this.options.name,
|