@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 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 branch and diff
10
- data, and confirms storage APIs. Swap in your own private key path when running outside this
11
- workstation and adjust `-e`/`-s` for non-production environments.
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 repository), use
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(`Run: git remote add ephemeral ${await repo.getEphemeralRemoteURL()}`);
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, ArrayBuffers, `Blob`
228
- or `File` objects, `ReadableStream`s, or iterable/async-iterable sources.
229
- - `addFileFromString(path, contents, options)` for text helpers (defaults to UTF-8 and accepts any
230
- Node.js `BufferEncoding`).
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 `expectedHeadSha`) the
252
- builder throws a `RefUpdateError` containing the status, reason, and ref details.
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 the commit.
257
- - `expectedHeadSha` (optional): Commit SHA that must match the remote tip; omit to fast-forward
258
- unconditionally.
259
- - `baseBranch` (optional): Mirrors the `base_branch` metadata and names an existing branch whose tip
260
- should seed `targetBranch` if it does not exist. Leave `expectedHeadSha` empty when creating a new
261
- branch from `baseBranch`; when both are provided and the branch already exists, `expectedHeadSha`
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 identity is reused.
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 `refs/heads/main`). Prefer
268
- `targetBranch`, which now accepts plain branch names.
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 assets without
271
- > buffering them entirely in memory. File paths are normalized relative to the repository root.
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 provide `baseBranch` (or
274
- > the repository has no refs). To seed an empty repository, point to the default branch and omit
275
- > `expectedHeadSha`. To create a missing branch within an existing repository, set `baseBranch` to
276
- > the source branch and omit `expectedHeadSha` so the service clones that tip before applying your
277
- > changes.
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 can stream it to
282
- the gateway with a single call. The SDK handles chunking and NDJSON streaming just like it does for
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`, `File`, `ReadableStream`,
303
- iterable, or async iterable of byte chunks—the same sources supported by the standard commit
304
- builder. `createCommitFromDiff` returns a `Promise<CommitResult>` and throws a `RefUpdateError` when
305
- the server rejects the diff (for example, if the branch tip changed).
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 assets without
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 `getRemoteURL()`, it:
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 necessary
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` when the backend
671
- reports a ref failure. Inspect `error.status`, `error.reason`, `error.message`, and
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({ blob_count: true });
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(result.branch, result.old_sha, result.new_sha);
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.9.3"};
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("createCommit baseBranch must not include refs/ prefix");
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("createCommitFromDiff author name and email are required");
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("createCommitFromDiff baseBranch must not include refs/ prefix");
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(request.metadata, request.diffChunks);
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("createCommitFromDiff committer name and email are required when provided");
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("createCommitFromDiff targetBranch must include a branch name");
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("createCommitFromDiff targetBranch must not include refs/ prefix");
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(result.branch, result.old_sha, result.new_sha)
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(`${baseUrl}--${version}`, new ApiFetcher(baseUrl, version));
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(`https://${this.options.storageBaseUrl}/${this.id}.git`);
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(`https://${this.options.storageBaseUrl}/${this.id}+ephemeral.git`);
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
- { path: "repos/files", params: Object.keys(params).length ? params : void 0 },
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({ path: "repos/branches", params }, jwt);
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({ path: "repos/notes", params: { sha } }, jwt);
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(result.targetRef, result.baseCommit, result.newRefSha)
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(result.targetRef, result.baseCommit, result.newRefSha)
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("deleteNote author name and email are required when provided");
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(result.targetRef, result.baseCommit, result.newRefSha)
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({ path: "repos/branches/diff", params }, jwt);
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
- if (options.ref) {
2032
- body.rev = options.ref;
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({ path: "repos/pull-upstream", body }, jwt);
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(`Pull Upstream failed: ${response.status} ${await response.text()}`);
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({ path: "repos/branches/create", body }, jwt);
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("restoreCommit targetBranch must not include refs/ prefix");
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("restoreCommit committer name and email are required when provided");
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 && { default_branch: resolvedDefaultBranch }
2406
+ ...resolvedDefaultBranch && {
2407
+ default_branch: resolvedDefaultBranch
2408
+ }
2315
2409
  }
2316
2410
  } : "repos";
2317
- const resp = await this.api.post(createRepoPath, jwt, { allowedStatus: [409] });
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
- return new RepoImpl(options.id, defaultBranch, this.options, this.generateJWT.bind(this));
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, { allowedStatus: [404, 409] });
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(options, this.options.defaultTTL ?? 365 * 24 * 60 * 60);
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,