@pierre/storage 0.0.10 → 0.1.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/dist/index.js CHANGED
@@ -1,14 +1,658 @@
1
1
  import { importPKCS8, SignJWT } from 'jose';
2
2
  import snakecaseKeys from 'snakecase-keys';
3
+ import { z } from 'zod';
3
4
 
4
5
  // src/index.ts
5
6
 
7
+ // src/errors.ts
8
+ var RefUpdateError = class extends Error {
9
+ status;
10
+ reason;
11
+ refUpdate;
12
+ constructor(message, options) {
13
+ super(message);
14
+ this.name = "RefUpdateError";
15
+ this.status = options.status;
16
+ this.reason = options.reason ?? inferRefUpdateReason(options.status);
17
+ this.refUpdate = options.refUpdate;
18
+ }
19
+ };
20
+ var REF_REASON_MAP = {
21
+ precondition_failed: "precondition_failed",
22
+ conflict: "conflict",
23
+ not_found: "not_found",
24
+ invalid: "invalid",
25
+ timeout: "timeout",
26
+ unauthorized: "unauthorized",
27
+ forbidden: "forbidden",
28
+ unavailable: "unavailable",
29
+ internal: "internal",
30
+ failed: "failed",
31
+ ok: "unknown"
32
+ };
33
+ function inferRefUpdateReason(status) {
34
+ if (!status) {
35
+ return "unknown";
36
+ }
37
+ const trimmed = status.trim();
38
+ if (trimmed === "") {
39
+ return "unknown";
40
+ }
41
+ const label = trimmed.toLowerCase();
42
+ return REF_REASON_MAP[label] ?? "unknown";
43
+ }
44
+ var listFilesResponseSchema = z.object({
45
+ paths: z.array(z.string()),
46
+ ref: z.string()
47
+ });
48
+ var branchInfoSchema = z.object({
49
+ cursor: z.string(),
50
+ name: z.string(),
51
+ head_sha: z.string(),
52
+ created_at: z.string()
53
+ });
54
+ var listBranchesResponseSchema = z.object({
55
+ branches: z.array(branchInfoSchema),
56
+ next_cursor: z.string().nullable().optional(),
57
+ has_more: z.boolean()
58
+ });
59
+ var commitInfoRawSchema = z.object({
60
+ sha: z.string(),
61
+ message: z.string(),
62
+ author_name: z.string(),
63
+ author_email: z.string(),
64
+ committer_name: z.string(),
65
+ committer_email: z.string(),
66
+ date: z.string()
67
+ });
68
+ var listCommitsResponseSchema = z.object({
69
+ commits: z.array(commitInfoRawSchema),
70
+ next_cursor: z.string().nullable().optional(),
71
+ has_more: z.boolean()
72
+ });
73
+ var diffStatsSchema = z.object({
74
+ files: z.number(),
75
+ additions: z.number(),
76
+ deletions: z.number(),
77
+ changes: z.number()
78
+ });
79
+ var diffFileRawSchema = z.object({
80
+ path: z.string(),
81
+ state: z.string(),
82
+ old_path: z.string().nullable().optional(),
83
+ raw: z.string(),
84
+ bytes: z.number(),
85
+ is_eof: z.boolean()
86
+ });
87
+ var filteredFileRawSchema = z.object({
88
+ path: z.string(),
89
+ state: z.string(),
90
+ old_path: z.string().nullable().optional(),
91
+ bytes: z.number(),
92
+ is_eof: z.boolean()
93
+ });
94
+ var branchDiffResponseSchema = z.object({
95
+ branch: z.string(),
96
+ base: z.string(),
97
+ stats: diffStatsSchema,
98
+ files: z.array(diffFileRawSchema),
99
+ filtered_files: z.array(filteredFileRawSchema)
100
+ });
101
+ var commitDiffResponseSchema = z.object({
102
+ sha: z.string(),
103
+ stats: diffStatsSchema,
104
+ files: z.array(diffFileRawSchema),
105
+ filtered_files: z.array(filteredFileRawSchema)
106
+ });
107
+ var refUpdateResultSchema = z.object({
108
+ branch: z.string(),
109
+ old_sha: z.string(),
110
+ new_sha: z.string(),
111
+ success: z.boolean(),
112
+ status: z.string(),
113
+ message: z.string().optional()
114
+ });
115
+ var commitPackCommitSchema = z.object({
116
+ commit_sha: z.string(),
117
+ tree_sha: z.string(),
118
+ target_branch: z.string(),
119
+ pack_bytes: z.number(),
120
+ blob_count: z.number()
121
+ });
122
+ var resetCommitCommitSchema = commitPackCommitSchema.omit({ blob_count: true });
123
+ var refUpdateResultWithOptionalsSchema = z.object({
124
+ branch: z.string().optional(),
125
+ old_sha: z.string().optional(),
126
+ new_sha: z.string().optional(),
127
+ success: z.boolean().optional(),
128
+ status: z.string(),
129
+ message: z.string().optional()
130
+ });
131
+ var commitPackAckSchema = z.object({
132
+ commit: commitPackCommitSchema,
133
+ result: refUpdateResultSchema
134
+ });
135
+ var resetCommitAckSchema = z.object({
136
+ commit: resetCommitCommitSchema,
137
+ result: refUpdateResultSchema.extend({ success: z.literal(true) })
138
+ });
139
+ var commitPackResponseSchema = z.object({
140
+ commit: commitPackCommitSchema.partial().optional().nullable(),
141
+ result: refUpdateResultWithOptionalsSchema
142
+ });
143
+ var resetCommitResponseSchema = z.object({
144
+ commit: resetCommitCommitSchema.partial().optional().nullable(),
145
+ result: refUpdateResultWithOptionalsSchema
146
+ });
147
+ var errorEnvelopeSchema = z.object({
148
+ error: z.string()
149
+ });
150
+
151
+ // src/commit.ts
152
+ var MAX_CHUNK_BYTES = 4 * 1024 * 1024;
153
+ var DEFAULT_TTL_SECONDS = 60 * 60;
154
+ var BufferCtor = globalThis.Buffer;
155
+ var CommitBuilderImpl = class {
156
+ options;
157
+ getAuthToken;
158
+ transport;
159
+ operations = [];
160
+ sent = false;
161
+ constructor(deps) {
162
+ this.options = { ...deps.options };
163
+ this.getAuthToken = deps.getAuthToken;
164
+ this.transport = deps.transport;
165
+ const trimmedTarget = this.options.targetBranch?.trim();
166
+ const trimmedMessage = this.options.commitMessage?.trim();
167
+ const trimmedAuthorName = this.options.author?.name?.trim();
168
+ const trimmedAuthorEmail = this.options.author?.email?.trim();
169
+ if (!trimmedTarget) {
170
+ throw new Error("createCommit targetBranch is required");
171
+ }
172
+ if (trimmedTarget.startsWith("refs/")) {
173
+ throw new Error("createCommit targetBranch must not include refs/ prefix");
174
+ }
175
+ if (!trimmedMessage) {
176
+ throw new Error("createCommit commitMessage is required");
177
+ }
178
+ if (!trimmedAuthorName || !trimmedAuthorEmail) {
179
+ throw new Error("createCommit author name and email are required");
180
+ }
181
+ this.options.targetBranch = trimmedTarget;
182
+ this.options.commitMessage = trimmedMessage;
183
+ this.options.author = {
184
+ name: trimmedAuthorName,
185
+ email: trimmedAuthorEmail
186
+ };
187
+ if (typeof this.options.expectedHeadSha === "string") {
188
+ this.options.expectedHeadSha = this.options.expectedHeadSha.trim();
189
+ }
190
+ }
191
+ addFile(path, source, options) {
192
+ this.ensureNotSent();
193
+ const normalizedPath = this.normalizePath(path);
194
+ const contentId = randomContentId();
195
+ const mode = options?.mode ?? "100644";
196
+ this.operations.push({
197
+ path: normalizedPath,
198
+ contentId,
199
+ mode,
200
+ operation: "upsert",
201
+ streamFactory: () => toAsyncIterable(source)
202
+ });
203
+ return this;
204
+ }
205
+ addFileFromString(path, contents, options) {
206
+ const encoding = options?.encoding ?? "utf8";
207
+ const normalizedEncoding = encoding === "utf-8" ? "utf8" : encoding;
208
+ let data;
209
+ if (normalizedEncoding === "utf8") {
210
+ data = new TextEncoder().encode(contents);
211
+ } else if (BufferCtor) {
212
+ data = BufferCtor.from(
213
+ contents,
214
+ normalizedEncoding
215
+ );
216
+ } else {
217
+ throw new Error(
218
+ `Unsupported encoding "${encoding}" in this environment. Non-UTF encodings require Node.js Buffer support.`
219
+ );
220
+ }
221
+ return this.addFile(path, data, options);
222
+ }
223
+ deletePath(path) {
224
+ this.ensureNotSent();
225
+ const normalizedPath = this.normalizePath(path);
226
+ this.operations.push({
227
+ path: normalizedPath,
228
+ contentId: randomContentId(),
229
+ operation: "delete"
230
+ });
231
+ return this;
232
+ }
233
+ async send() {
234
+ this.ensureNotSent();
235
+ this.sent = true;
236
+ const metadata = this.buildMetadata();
237
+ const blobEntries = this.operations.filter((op) => op.operation === "upsert" && op.streamFactory).map((op) => ({
238
+ contentId: op.contentId,
239
+ chunks: chunkify(op.streamFactory())
240
+ }));
241
+ const authorization = await this.getAuthToken();
242
+ const ack = await this.transport.send({
243
+ authorization,
244
+ signal: this.options.signal,
245
+ metadata,
246
+ blobs: blobEntries
247
+ });
248
+ return buildCommitResult(ack);
249
+ }
250
+ buildMetadata() {
251
+ const files = this.operations.map((op) => {
252
+ const entry = {
253
+ path: op.path,
254
+ content_id: op.contentId,
255
+ operation: op.operation
256
+ };
257
+ if (op.mode) {
258
+ entry.mode = op.mode;
259
+ }
260
+ return entry;
261
+ });
262
+ const metadata = {
263
+ target_branch: this.options.targetBranch,
264
+ commit_message: this.options.commitMessage,
265
+ author: {
266
+ name: this.options.author.name,
267
+ email: this.options.author.email
268
+ },
269
+ files
270
+ };
271
+ if (this.options.expectedHeadSha) {
272
+ metadata.expected_head_sha = this.options.expectedHeadSha;
273
+ }
274
+ if (this.options.committer) {
275
+ metadata.committer = {
276
+ name: this.options.committer.name,
277
+ email: this.options.committer.email
278
+ };
279
+ }
280
+ return metadata;
281
+ }
282
+ ensureNotSent() {
283
+ if (this.sent) {
284
+ throw new Error("createCommit builder cannot be reused after send()");
285
+ }
286
+ }
287
+ normalizePath(path) {
288
+ if (!path || typeof path !== "string" || path.trim() === "") {
289
+ throw new Error("File path must be a non-empty string");
290
+ }
291
+ return path.replace(/^\//, "");
292
+ }
293
+ };
294
+ var FetchCommitTransport = class {
295
+ url;
296
+ constructor(config) {
297
+ const trimmedBase = config.baseUrl.replace(/\/+$/, "");
298
+ this.url = `${trimmedBase}/api/v${config.version}/repos/commit-pack`;
299
+ }
300
+ async send(request) {
301
+ const bodyIterable = buildMessageIterable(request.metadata, request.blobs);
302
+ const body = toRequestBody(bodyIterable);
303
+ const response = await fetch(this.url, {
304
+ method: "POST",
305
+ headers: {
306
+ Authorization: `Bearer ${request.authorization}`,
307
+ "Content-Type": "application/x-ndjson",
308
+ Accept: "application/json"
309
+ },
310
+ body,
311
+ signal: request.signal
312
+ });
313
+ if (!response.ok) {
314
+ const { statusMessage, statusLabel, refUpdate } = await parseCommitPackError(response);
315
+ throw new RefUpdateError(statusMessage, {
316
+ status: statusLabel,
317
+ message: statusMessage,
318
+ refUpdate
319
+ });
320
+ }
321
+ const ack = commitPackAckSchema.parse(await response.json());
322
+ return ack;
323
+ }
324
+ };
325
+ function toRequestBody(iterable) {
326
+ const readableStreamCtor = globalThis.ReadableStream;
327
+ if (typeof readableStreamCtor === "function") {
328
+ const iterator = iterable[Symbol.asyncIterator]();
329
+ return new readableStreamCtor({
330
+ async pull(controller) {
331
+ const { value, done } = await iterator.next();
332
+ if (done) {
333
+ controller.close();
334
+ return;
335
+ }
336
+ controller.enqueue(value);
337
+ },
338
+ async cancel(reason) {
339
+ if (typeof iterator.return === "function") {
340
+ await iterator.return(reason);
341
+ }
342
+ }
343
+ });
344
+ }
345
+ return iterable;
346
+ }
347
+ function buildMessageIterable(metadata, blobs) {
348
+ const encoder = new TextEncoder();
349
+ return {
350
+ async *[Symbol.asyncIterator]() {
351
+ yield encoder.encode(`${JSON.stringify({ metadata })}
352
+ `);
353
+ for (const blob of blobs) {
354
+ for await (const segment of blob.chunks) {
355
+ const payload = {
356
+ blob_chunk: {
357
+ content_id: blob.contentId,
358
+ data: base64Encode(segment.chunk),
359
+ eof: segment.eof
360
+ }
361
+ };
362
+ yield encoder.encode(`${JSON.stringify(payload)}
363
+ `);
364
+ }
365
+ }
366
+ }
367
+ };
368
+ }
369
+ function buildCommitResult(ack) {
370
+ const refUpdate = toRefUpdate(ack.result);
371
+ if (!ack.result.success) {
372
+ throw new RefUpdateError(
373
+ ack.result.message ?? `Commit failed with status ${ack.result.status}`,
374
+ {
375
+ status: ack.result.status,
376
+ message: ack.result.message,
377
+ refUpdate
378
+ }
379
+ );
380
+ }
381
+ return {
382
+ commitSha: ack.commit.commit_sha,
383
+ treeSha: ack.commit.tree_sha,
384
+ targetBranch: ack.commit.target_branch,
385
+ packBytes: ack.commit.pack_bytes,
386
+ blobCount: ack.commit.blob_count,
387
+ refUpdate
388
+ };
389
+ }
390
+ function toRefUpdate(result) {
391
+ return {
392
+ branch: result.branch,
393
+ oldSha: result.old_sha,
394
+ newSha: result.new_sha
395
+ };
396
+ }
397
+ async function* chunkify(source) {
398
+ let pending = null;
399
+ let produced = false;
400
+ for await (const value of source) {
401
+ const bytes = value;
402
+ if (pending && pending.byteLength === MAX_CHUNK_BYTES) {
403
+ yield { chunk: pending, eof: false };
404
+ produced = true;
405
+ pending = null;
406
+ }
407
+ const merged = pending ? concatChunks(pending, bytes) : bytes;
408
+ pending = null;
409
+ let cursor = merged;
410
+ while (cursor.byteLength > MAX_CHUNK_BYTES) {
411
+ const chunk = cursor.slice(0, MAX_CHUNK_BYTES);
412
+ cursor = cursor.slice(MAX_CHUNK_BYTES);
413
+ yield { chunk, eof: false };
414
+ produced = true;
415
+ }
416
+ pending = cursor;
417
+ }
418
+ if (pending) {
419
+ yield { chunk: pending, eof: true };
420
+ produced = true;
421
+ }
422
+ if (!produced) {
423
+ yield { chunk: new Uint8Array(0), eof: true };
424
+ }
425
+ }
426
+ async function* toAsyncIterable(source) {
427
+ if (typeof source === "string") {
428
+ yield new TextEncoder().encode(source);
429
+ return;
430
+ }
431
+ if (source instanceof Uint8Array) {
432
+ yield source;
433
+ return;
434
+ }
435
+ if (source instanceof ArrayBuffer) {
436
+ yield new Uint8Array(source);
437
+ return;
438
+ }
439
+ if (ArrayBuffer.isView(source)) {
440
+ yield new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
441
+ return;
442
+ }
443
+ if (isBlobLike(source)) {
444
+ const stream = source.stream();
445
+ if (isAsyncIterable(stream)) {
446
+ for await (const chunk of stream) {
447
+ yield ensureUint8Array(chunk);
448
+ }
449
+ return;
450
+ }
451
+ if (isReadableStreamLike(stream)) {
452
+ yield* readReadableStream(stream);
453
+ return;
454
+ }
455
+ }
456
+ if (isReadableStreamLike(source)) {
457
+ yield* readReadableStream(source);
458
+ return;
459
+ }
460
+ if (isAsyncIterable(source)) {
461
+ for await (const chunk of source) {
462
+ yield ensureUint8Array(chunk);
463
+ }
464
+ return;
465
+ }
466
+ if (isIterable(source)) {
467
+ for (const chunk of source) {
468
+ yield ensureUint8Array(chunk);
469
+ }
470
+ return;
471
+ }
472
+ throw new Error("Unsupported file source for createCommit");
473
+ }
474
+ async function* readReadableStream(stream) {
475
+ const reader = stream.getReader();
476
+ try {
477
+ while (true) {
478
+ const { value, done } = await reader.read();
479
+ if (done) {
480
+ break;
481
+ }
482
+ if (value !== void 0) {
483
+ yield ensureUint8Array(value);
484
+ }
485
+ }
486
+ } finally {
487
+ reader.releaseLock?.();
488
+ }
489
+ }
490
+ function ensureUint8Array(value) {
491
+ if (value instanceof Uint8Array) {
492
+ return value;
493
+ }
494
+ if (value instanceof ArrayBuffer) {
495
+ return new Uint8Array(value);
496
+ }
497
+ if (ArrayBuffer.isView(value)) {
498
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
499
+ }
500
+ if (typeof value === "string") {
501
+ return new TextEncoder().encode(value);
502
+ }
503
+ if (BufferCtor && BufferCtor.isBuffer(value)) {
504
+ return value;
505
+ }
506
+ throw new Error("Unsupported chunk type; expected binary data");
507
+ }
508
+ function isBlobLike(value) {
509
+ return typeof value === "object" && value !== null && typeof value.stream === "function";
510
+ }
511
+ function isReadableStreamLike(value) {
512
+ return typeof value === "object" && value !== null && typeof value.getReader === "function";
513
+ }
514
+ function isAsyncIterable(value) {
515
+ return typeof value === "object" && value !== null && Symbol.asyncIterator in value;
516
+ }
517
+ function isIterable(value) {
518
+ return typeof value === "object" && value !== null && Symbol.iterator in value;
519
+ }
520
+ function concatChunks(a, b) {
521
+ if (a.byteLength === 0) {
522
+ return b;
523
+ }
524
+ if (b.byteLength === 0) {
525
+ return a;
526
+ }
527
+ const merged = new Uint8Array(a.byteLength + b.byteLength);
528
+ merged.set(a, 0);
529
+ merged.set(b, a.byteLength);
530
+ return merged;
531
+ }
532
+ function base64Encode(bytes) {
533
+ if (BufferCtor) {
534
+ return BufferCtor.from(bytes).toString("base64");
535
+ }
536
+ let binary = "";
537
+ for (let i = 0; i < bytes.byteLength; i++) {
538
+ binary += String.fromCharCode(bytes[i]);
539
+ }
540
+ const btoaFn = globalThis.btoa;
541
+ if (typeof btoaFn === "function") {
542
+ return btoaFn(binary);
543
+ }
544
+ throw new Error("Base64 encoding is not supported in this environment");
545
+ }
546
+ function randomContentId() {
547
+ const cryptoObj = globalThis.crypto;
548
+ if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
549
+ return cryptoObj.randomUUID();
550
+ }
551
+ const random = Math.random().toString(36).slice(2);
552
+ return `cid-${Date.now().toString(36)}-${random}`;
553
+ }
554
+ function createCommitBuilder(deps) {
555
+ return new CommitBuilderImpl(deps);
556
+ }
557
+ function resolveCommitTtlSeconds(options) {
558
+ if (typeof options?.ttl === "number" && options.ttl > 0) {
559
+ return options.ttl;
560
+ }
561
+ return DEFAULT_TTL_SECONDS;
562
+ }
563
+ async function parseCommitPackError(response) {
564
+ const fallbackMessage = `createCommit request failed (${response.status} ${response.statusText})`;
565
+ const cloned = response.clone();
566
+ let jsonBody;
567
+ try {
568
+ jsonBody = await cloned.json();
569
+ } catch {
570
+ jsonBody = void 0;
571
+ }
572
+ let textBody;
573
+ if (jsonBody === void 0) {
574
+ try {
575
+ textBody = await response.text();
576
+ } catch {
577
+ textBody = void 0;
578
+ }
579
+ }
580
+ const defaultStatus = (() => {
581
+ const inferred = inferRefUpdateReason(String(response.status));
582
+ return inferred === "unknown" ? "failed" : inferred;
583
+ })();
584
+ let statusLabel = defaultStatus;
585
+ let refUpdate;
586
+ let message;
587
+ if (jsonBody !== void 0) {
588
+ const parsedResponse = commitPackResponseSchema.safeParse(jsonBody);
589
+ if (parsedResponse.success) {
590
+ const result = parsedResponse.data.result;
591
+ if (typeof result.status === "string" && result.status.trim() !== "") {
592
+ statusLabel = result.status.trim();
593
+ }
594
+ refUpdate = toPartialRefUpdateFields(result.branch, result.old_sha, result.new_sha);
595
+ if (typeof result.message === "string" && result.message.trim() !== "") {
596
+ message = result.message.trim();
597
+ }
598
+ }
599
+ if (!message) {
600
+ const parsedError = errorEnvelopeSchema.safeParse(jsonBody);
601
+ if (parsedError.success) {
602
+ const trimmed = parsedError.data.error.trim();
603
+ if (trimmed) {
604
+ message = trimmed;
605
+ }
606
+ }
607
+ }
608
+ }
609
+ if (!message && typeof jsonBody === "string" && jsonBody.trim() !== "") {
610
+ message = jsonBody.trim();
611
+ }
612
+ if (!message && textBody && textBody.trim() !== "") {
613
+ message = textBody.trim();
614
+ }
615
+ return {
616
+ statusMessage: message ?? fallbackMessage,
617
+ statusLabel,
618
+ refUpdate
619
+ };
620
+ }
621
+ function toPartialRefUpdateFields(branch, oldSha, newSha) {
622
+ const refUpdate = {};
623
+ if (typeof branch === "string" && branch.trim() !== "") {
624
+ refUpdate.branch = branch.trim();
625
+ }
626
+ if (typeof oldSha === "string" && oldSha.trim() !== "") {
627
+ refUpdate.oldSha = oldSha.trim();
628
+ }
629
+ if (typeof newSha === "string" && newSha.trim() !== "") {
630
+ refUpdate.newSha = newSha.trim();
631
+ }
632
+ return Object.keys(refUpdate).length > 0 ? refUpdate : void 0;
633
+ }
634
+
6
635
  // src/fetch.ts
636
+ var ApiError = class extends Error {
637
+ status;
638
+ statusText;
639
+ method;
640
+ url;
641
+ body;
642
+ constructor(params) {
643
+ super(params.message);
644
+ this.name = "ApiError";
645
+ this.status = params.status;
646
+ this.statusText = params.statusText;
647
+ this.method = params.method;
648
+ this.url = params.url;
649
+ this.body = params.body;
650
+ }
651
+ };
7
652
  var ApiFetcher = class {
8
653
  constructor(API_BASE_URL2, version) {
9
654
  this.API_BASE_URL = API_BASE_URL2;
10
655
  this.version = version;
11
- console.log("api fetcher created", API_BASE_URL2, version);
12
656
  }
13
657
  getBaseUrl() {
14
658
  return `${this.API_BASE_URL}/api/v${this.version}`;
@@ -38,9 +682,48 @@ var ApiFetcher = class {
38
682
  const response = await fetch(requestUrl, requestOptions);
39
683
  if (!response.ok) {
40
684
  const allowed = options?.allowedStatus ?? [];
41
- if (!allowed.includes(response.status)) {
42
- throw new Error(`Failed to fetch ${method} ${requestUrl}: ${response.statusText}`);
685
+ if (allowed.includes(response.status)) {
686
+ return response;
687
+ }
688
+ let errorBody;
689
+ let message;
690
+ const contentType = response.headers.get("content-type") ?? "";
691
+ try {
692
+ if (contentType.includes("application/json")) {
693
+ errorBody = await response.json();
694
+ } else {
695
+ const text = await response.text();
696
+ errorBody = text;
697
+ }
698
+ } catch {
699
+ try {
700
+ errorBody = await response.text();
701
+ } catch {
702
+ errorBody = void 0;
703
+ }
43
704
  }
705
+ if (typeof errorBody === "string") {
706
+ const trimmed = errorBody.trim();
707
+ if (trimmed) {
708
+ message = trimmed;
709
+ }
710
+ } else if (errorBody && typeof errorBody === "object") {
711
+ const parsedError = errorEnvelopeSchema.safeParse(errorBody);
712
+ if (parsedError.success) {
713
+ const trimmed = parsedError.data.error.trim();
714
+ if (trimmed) {
715
+ message = trimmed;
716
+ }
717
+ }
718
+ }
719
+ throw new ApiError({
720
+ message: message ?? `Request ${method} ${requestUrl} failed with status ${response.status} ${response.statusText}`,
721
+ status: response.status,
722
+ statusText: response.statusText,
723
+ method,
724
+ url: requestUrl,
725
+ body: errorBody
726
+ });
44
727
  }
45
728
  return response;
46
729
  }
@@ -213,9 +896,9 @@ async function validateWebhook(payload, headers, secret, options = {}) {
213
896
  return validationResult;
214
897
  }
215
898
  const payloadStr = typeof payload === "string" ? payload : payload.toString("utf8");
216
- let parsedPayload;
899
+ let parsedJson;
217
900
  try {
218
- parsedPayload = JSON.parse(payloadStr);
901
+ parsedJson = JSON.parse(payloadStr);
219
902
  } catch {
220
903
  return {
221
904
  valid: false,
@@ -223,25 +906,283 @@ async function validateWebhook(payload, headers, secret, options = {}) {
223
906
  timestamp: validationResult.timestamp
224
907
  };
225
908
  }
909
+ const conversion = convertWebhookPayload(String(eventType), parsedJson);
910
+ if (!conversion.valid) {
911
+ return {
912
+ valid: false,
913
+ error: conversion.error,
914
+ timestamp: validationResult.timestamp
915
+ };
916
+ }
226
917
  return {
227
918
  valid: true,
228
919
  eventType,
229
920
  timestamp: validationResult.timestamp,
230
- payload: parsedPayload
921
+ payload: conversion.payload
922
+ };
923
+ }
924
+ function convertWebhookPayload(eventType, raw) {
925
+ if (eventType === "push") {
926
+ if (!isRawWebhookPushEvent(raw)) {
927
+ return {
928
+ valid: false,
929
+ error: "Invalid push payload"
930
+ };
931
+ }
932
+ return {
933
+ valid: true,
934
+ payload: transformPushEvent(raw)
935
+ };
936
+ }
937
+ const fallbackPayload = { type: eventType, raw };
938
+ return {
939
+ valid: true,
940
+ payload: fallbackPayload
941
+ };
942
+ }
943
+ function transformPushEvent(raw) {
944
+ return {
945
+ type: "push",
946
+ repository: {
947
+ id: raw.repository.id,
948
+ url: raw.repository.url
949
+ },
950
+ ref: raw.ref,
951
+ before: raw.before,
952
+ after: raw.after,
953
+ customerId: raw.customer_id,
954
+ pushedAt: new Date(raw.pushed_at),
955
+ rawPushedAt: raw.pushed_at
231
956
  };
232
957
  }
958
+ function isRawWebhookPushEvent(value) {
959
+ if (!isRecord(value)) {
960
+ return false;
961
+ }
962
+ if (!isRecord(value.repository)) {
963
+ return false;
964
+ }
965
+ return typeof value.repository.id === "string" && typeof value.repository.url === "string" && typeof value.ref === "string" && typeof value.before === "string" && typeof value.after === "string" && typeof value.customer_id === "string" && typeof value.pushed_at === "string";
966
+ }
967
+ function isRecord(value) {
968
+ return typeof value === "object" && value !== null;
969
+ }
233
970
 
234
971
  // src/index.ts
235
- var API_BASE_URL = "https://api.git.storage";
236
- var STORAGE_BASE_URL = "git.storage";
972
+ var API_BASE_URL = "https://api.code.storage";
973
+ var STORAGE_BASE_URL = "code.storage";
237
974
  var API_VERSION = 1;
238
975
  var apiInstanceMap = /* @__PURE__ */ new Map();
976
+ var DEFAULT_TOKEN_TTL_SECONDS = 60 * 60;
977
+ var RESET_COMMIT_ALLOWED_STATUS = [
978
+ 400,
979
+ // Bad Request - validation errors
980
+ 401,
981
+ // Unauthorized - missing/invalid auth header
982
+ 403,
983
+ // Forbidden - missing git:write scope
984
+ 404,
985
+ // Not Found - repo lookup failures
986
+ 408,
987
+ // Request Timeout - client cancelled
988
+ 409,
989
+ // Conflict - concurrent ref updates
990
+ 412,
991
+ // Precondition Failed - optimistic concurrency
992
+ 422,
993
+ // Unprocessable Entity - metadata issues
994
+ 429,
995
+ // Too Many Requests - upstream throttling
996
+ 499,
997
+ // Client Closed Request - storage cancellation
998
+ 500,
999
+ // Internal Server Error - generic failure
1000
+ 502,
1001
+ // Bad Gateway - storage/gateway bridge issues
1002
+ 503,
1003
+ // Service Unavailable - storage selection failures
1004
+ 504
1005
+ // Gateway Timeout - long-running storage operations
1006
+ ];
1007
+ function resolveInvocationTtlSeconds(options, defaultValue = DEFAULT_TOKEN_TTL_SECONDS) {
1008
+ if (typeof options?.ttl === "number" && options.ttl > 0) {
1009
+ return options.ttl;
1010
+ }
1011
+ return defaultValue;
1012
+ }
1013
+ function toRefUpdate2(result) {
1014
+ return {
1015
+ branch: result.branch,
1016
+ oldSha: result.old_sha,
1017
+ newSha: result.new_sha
1018
+ };
1019
+ }
1020
+ function buildResetCommitResult(ack) {
1021
+ const refUpdate = toRefUpdate2(ack.result);
1022
+ if (!ack.result.success) {
1023
+ throw new RefUpdateError(
1024
+ ack.result.message ?? `Reset commit failed with status ${ack.result.status}`,
1025
+ {
1026
+ status: ack.result.status,
1027
+ message: ack.result.message,
1028
+ refUpdate
1029
+ }
1030
+ );
1031
+ }
1032
+ return {
1033
+ commitSha: ack.commit.commit_sha,
1034
+ treeSha: ack.commit.tree_sha,
1035
+ targetBranch: ack.commit.target_branch,
1036
+ packBytes: ack.commit.pack_bytes,
1037
+ refUpdate
1038
+ };
1039
+ }
1040
+ function toPartialRefUpdate(branch, oldSha, newSha) {
1041
+ const refUpdate = {};
1042
+ if (typeof branch === "string" && branch.trim() !== "") {
1043
+ refUpdate.branch = branch;
1044
+ }
1045
+ if (typeof oldSha === "string" && oldSha.trim() !== "") {
1046
+ refUpdate.oldSha = oldSha;
1047
+ }
1048
+ if (typeof newSha === "string" && newSha.trim() !== "") {
1049
+ refUpdate.newSha = newSha;
1050
+ }
1051
+ return Object.keys(refUpdate).length > 0 ? refUpdate : void 0;
1052
+ }
1053
+ function parseResetCommitPayload(payload) {
1054
+ const ack = resetCommitAckSchema.safeParse(payload);
1055
+ if (ack.success) {
1056
+ return { ack: ack.data };
1057
+ }
1058
+ const failure = resetCommitResponseSchema.safeParse(payload);
1059
+ if (failure.success) {
1060
+ const result = failure.data.result;
1061
+ return {
1062
+ failure: {
1063
+ status: result.status,
1064
+ message: result.message,
1065
+ refUpdate: toPartialRefUpdate(result.branch, result.old_sha, result.new_sha)
1066
+ }
1067
+ };
1068
+ }
1069
+ return null;
1070
+ }
1071
+ function httpStatusToResetStatus(status) {
1072
+ switch (status) {
1073
+ case 409:
1074
+ return "conflict";
1075
+ case 412:
1076
+ return "precondition_failed";
1077
+ default:
1078
+ return `${status}`;
1079
+ }
1080
+ }
239
1081
  function getApiInstance(baseUrl, version) {
240
1082
  if (!apiInstanceMap.has(`${baseUrl}--${version}`)) {
241
1083
  apiInstanceMap.set(`${baseUrl}--${version}`, new ApiFetcher(baseUrl, version));
242
1084
  }
243
1085
  return apiInstanceMap.get(`${baseUrl}--${version}`);
244
1086
  }
1087
+ function transformBranchInfo(raw) {
1088
+ return {
1089
+ cursor: raw.cursor,
1090
+ name: raw.name,
1091
+ headSha: raw.head_sha,
1092
+ createdAt: raw.created_at
1093
+ };
1094
+ }
1095
+ function transformListBranchesResult(raw) {
1096
+ return {
1097
+ branches: raw.branches.map(transformBranchInfo),
1098
+ nextCursor: raw.next_cursor ?? void 0,
1099
+ hasMore: raw.has_more
1100
+ };
1101
+ }
1102
+ function transformCommitInfo(raw) {
1103
+ const parsedDate = new Date(raw.date);
1104
+ return {
1105
+ sha: raw.sha,
1106
+ message: raw.message,
1107
+ authorName: raw.author_name,
1108
+ authorEmail: raw.author_email,
1109
+ committerName: raw.committer_name,
1110
+ committerEmail: raw.committer_email,
1111
+ date: parsedDate,
1112
+ rawDate: raw.date
1113
+ };
1114
+ }
1115
+ function transformListCommitsResult(raw) {
1116
+ return {
1117
+ commits: raw.commits.map(transformCommitInfo),
1118
+ nextCursor: raw.next_cursor ?? void 0,
1119
+ hasMore: raw.has_more
1120
+ };
1121
+ }
1122
+ function normalizeDiffState(rawState) {
1123
+ if (!rawState) {
1124
+ return "unknown";
1125
+ }
1126
+ const leading = rawState.trim()[0]?.toUpperCase();
1127
+ switch (leading) {
1128
+ case "A":
1129
+ return "added";
1130
+ case "M":
1131
+ return "modified";
1132
+ case "D":
1133
+ return "deleted";
1134
+ case "R":
1135
+ return "renamed";
1136
+ case "C":
1137
+ return "copied";
1138
+ case "T":
1139
+ return "type_changed";
1140
+ case "U":
1141
+ return "unmerged";
1142
+ default:
1143
+ return "unknown";
1144
+ }
1145
+ }
1146
+ function transformFileDiff(raw) {
1147
+ const normalizedState = normalizeDiffState(raw.state);
1148
+ return {
1149
+ path: raw.path,
1150
+ state: normalizedState,
1151
+ rawState: raw.state,
1152
+ oldPath: raw.old_path ?? void 0,
1153
+ raw: raw.raw,
1154
+ bytes: raw.bytes,
1155
+ isEof: raw.is_eof
1156
+ };
1157
+ }
1158
+ function transformFilteredFile(raw) {
1159
+ const normalizedState = normalizeDiffState(raw.state);
1160
+ return {
1161
+ path: raw.path,
1162
+ state: normalizedState,
1163
+ rawState: raw.state,
1164
+ oldPath: raw.old_path ?? void 0,
1165
+ bytes: raw.bytes,
1166
+ isEof: raw.is_eof
1167
+ };
1168
+ }
1169
+ function transformBranchDiffResult(raw) {
1170
+ return {
1171
+ branch: raw.branch,
1172
+ base: raw.base,
1173
+ stats: raw.stats,
1174
+ files: raw.files.map(transformFileDiff),
1175
+ filteredFiles: raw.filtered_files.map(transformFilteredFile)
1176
+ };
1177
+ }
1178
+ function transformCommitDiffResult(raw) {
1179
+ return {
1180
+ sha: raw.sha,
1181
+ stats: raw.stats,
1182
+ files: raw.files.map(transformFileDiff),
1183
+ filteredFiles: raw.filtered_files.map(transformFilteredFile)
1184
+ };
1185
+ }
245
1186
  var RepoImpl = class {
246
1187
  constructor(id, options, generateJWT) {
247
1188
  this.id = id;
@@ -261,10 +1202,10 @@ var RepoImpl = class {
261
1202
  return url.toString();
262
1203
  }
263
1204
  async getFileStream(options) {
1205
+ const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
264
1206
  const jwt = await this.generateJWT(this.id, {
265
1207
  permissions: ["git:read"],
266
- ttl: options?.ttl ?? 1 * 60 * 60
267
- // 1hr in seconds
1208
+ ttl
268
1209
  });
269
1210
  const params = {
270
1211
  path: options.path
@@ -275,39 +1216,46 @@ var RepoImpl = class {
275
1216
  return this.api.get({ path: "repos/file", params }, jwt);
276
1217
  }
277
1218
  async listFiles(options) {
1219
+ const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
278
1220
  const jwt = await this.generateJWT(this.id, {
279
1221
  permissions: ["git:read"],
280
- ttl: options?.ttl ?? 1 * 60 * 60
281
- // 1hr in seconds
1222
+ ttl
282
1223
  });
283
1224
  const params = options?.ref ? { ref: options.ref } : void 0;
284
1225
  const response = await this.api.get({ path: "repos/files", params }, jwt);
285
- return await response.json();
1226
+ const raw = listFilesResponseSchema.parse(await response.json());
1227
+ return { paths: raw.paths, ref: raw.ref };
286
1228
  }
287
1229
  async listBranches(options) {
1230
+ const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
288
1231
  const jwt = await this.generateJWT(this.id, {
289
1232
  permissions: ["git:read"],
290
- ttl: options?.ttl ?? 1 * 60 * 60
291
- // 1hr in seconds
1233
+ ttl
292
1234
  });
1235
+ const cursor = options?.cursor;
1236
+ const limit = options?.limit;
293
1237
  let params;
294
- if (options?.cursor || !options?.limit) {
1238
+ if (typeof cursor === "string" || typeof limit === "number") {
295
1239
  params = {};
296
- if (options?.cursor) {
297
- params.cursor = options.cursor;
1240
+ if (typeof cursor === "string") {
1241
+ params.cursor = cursor;
298
1242
  }
299
- if (typeof options?.limit == "number") {
300
- params.limit = options.limit.toString();
1243
+ if (typeof limit === "number") {
1244
+ params.limit = limit.toString();
301
1245
  }
302
1246
  }
303
1247
  const response = await this.api.get({ path: "repos/branches", params }, jwt);
304
- return await response.json();
1248
+ const raw = listBranchesResponseSchema.parse(await response.json());
1249
+ return transformListBranchesResult({
1250
+ ...raw,
1251
+ next_cursor: raw.next_cursor ?? void 0
1252
+ });
305
1253
  }
306
1254
  async listCommits(options) {
1255
+ const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
307
1256
  const jwt = await this.generateJWT(this.id, {
308
1257
  permissions: ["git:read"],
309
- ttl: options?.ttl ?? 1 * 60 * 60
310
- // 1hr in seconds
1258
+ ttl
311
1259
  });
312
1260
  let params;
313
1261
  if (options?.branch || options?.cursor || options?.limit) {
@@ -323,13 +1271,17 @@ var RepoImpl = class {
323
1271
  }
324
1272
  }
325
1273
  const response = await this.api.get({ path: "repos/commits", params }, jwt);
326
- return await response.json();
1274
+ const raw = listCommitsResponseSchema.parse(await response.json());
1275
+ return transformListCommitsResult({
1276
+ ...raw,
1277
+ next_cursor: raw.next_cursor ?? void 0
1278
+ });
327
1279
  }
328
1280
  async getBranchDiff(options) {
1281
+ const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
329
1282
  const jwt = await this.generateJWT(this.id, {
330
1283
  permissions: ["git:read"],
331
- ttl: options?.ttl ?? 1 * 60 * 60
332
- // 1hr in seconds
1284
+ ttl
333
1285
  });
334
1286
  const params = {
335
1287
  branch: options.branch
@@ -338,38 +1290,27 @@ var RepoImpl = class {
338
1290
  params.base = options.base;
339
1291
  }
340
1292
  const response = await this.api.get({ path: "repos/branches/diff", params }, jwt);
341
- return await response.json();
1293
+ const raw = branchDiffResponseSchema.parse(await response.json());
1294
+ return transformBranchDiffResult(raw);
342
1295
  }
343
1296
  async getCommitDiff(options) {
1297
+ const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
344
1298
  const jwt = await this.generateJWT(this.id, {
345
1299
  permissions: ["git:read"],
346
- ttl: options?.ttl ?? 1 * 60 * 60
347
- // 1hr in seconds
1300
+ ttl
348
1301
  });
349
1302
  const params = {
350
1303
  sha: options.sha
351
1304
  };
352
1305
  const response = await this.api.get({ path: "repos/diff", params }, jwt);
353
- return await response.json();
354
- }
355
- async getCommit(options) {
356
- const jwt = await this.generateJWT(this.id, {
357
- permissions: ["git:read"],
358
- ttl: options?.ttl ?? 1 * 60 * 60
359
- // 1hr in seconds
360
- });
361
- const params = {
362
- repo: this.id,
363
- sha: options.sha
364
- };
365
- const response = await this.api.get({ path: "commit", params }, jwt);
366
- return await response.json();
1306
+ const raw = commitDiffResponseSchema.parse(await response.json());
1307
+ return transformCommitDiffResult(raw);
367
1308
  }
368
1309
  async pullUpstream(options) {
1310
+ const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
369
1311
  const jwt = await this.generateJWT(this.id, {
370
1312
  permissions: ["git:write"],
371
- ttl: options?.ttl ?? 1 * 60 * 60
372
- // 1hr in seconds
1313
+ ttl
373
1314
  });
374
1315
  const body = {};
375
1316
  if (options.ref) {
@@ -381,6 +1322,90 @@ var RepoImpl = class {
381
1322
  }
382
1323
  return;
383
1324
  }
1325
+ async resetCommit(options) {
1326
+ const targetBranch = options?.targetBranch?.trim();
1327
+ if (!targetBranch) {
1328
+ throw new Error("resetCommit targetBranch is required");
1329
+ }
1330
+ if (targetBranch.startsWith("refs/")) {
1331
+ throw new Error("resetCommit targetBranch must not include refs/ prefix");
1332
+ }
1333
+ const targetCommitSha = options?.targetCommitSha?.trim();
1334
+ if (!targetCommitSha) {
1335
+ throw new Error("resetCommit targetCommitSha is required");
1336
+ }
1337
+ const commitMessage = options?.commitMessage?.trim();
1338
+ const authorName = options.author?.name?.trim();
1339
+ const authorEmail = options.author?.email?.trim();
1340
+ if (!authorName || !authorEmail) {
1341
+ throw new Error("resetCommit author name and email are required");
1342
+ }
1343
+ const ttl = resolveCommitTtlSeconds(options);
1344
+ const jwt = await this.generateJWT(this.id, {
1345
+ permissions: ["git:write"],
1346
+ ttl
1347
+ });
1348
+ const metadata = {
1349
+ target_branch: targetBranch,
1350
+ target_commit_sha: targetCommitSha,
1351
+ author: {
1352
+ name: authorName,
1353
+ email: authorEmail
1354
+ }
1355
+ };
1356
+ if (commitMessage) {
1357
+ metadata.commit_message = commitMessage;
1358
+ }
1359
+ const expectedHeadSha = options.expectedHeadSha?.trim();
1360
+ if (expectedHeadSha) {
1361
+ metadata.expected_head_sha = expectedHeadSha;
1362
+ }
1363
+ if (options.committer) {
1364
+ const committerName = options.committer.name?.trim();
1365
+ const committerEmail = options.committer.email?.trim();
1366
+ if (!committerName || !committerEmail) {
1367
+ throw new Error("resetCommit committer name and email are required when provided");
1368
+ }
1369
+ metadata.committer = {
1370
+ name: committerName,
1371
+ email: committerEmail
1372
+ };
1373
+ }
1374
+ const response = await this.api.post({ path: "repos/reset-commits", body: { metadata } }, jwt, {
1375
+ allowedStatus: [...RESET_COMMIT_ALLOWED_STATUS]
1376
+ });
1377
+ const payload = await response.json();
1378
+ const parsed = parseResetCommitPayload(payload);
1379
+ if (parsed && "ack" in parsed) {
1380
+ return buildResetCommitResult(parsed.ack);
1381
+ }
1382
+ const failure = parsed && "failure" in parsed ? parsed.failure : void 0;
1383
+ const status = failure?.status ?? httpStatusToResetStatus(response.status);
1384
+ const message = failure?.message ?? `Reset commit failed with HTTP ${response.status}` + (response.statusText ? ` ${response.statusText}` : "");
1385
+ throw new RefUpdateError(message, {
1386
+ status,
1387
+ refUpdate: failure?.refUpdate
1388
+ });
1389
+ }
1390
+ createCommit(options) {
1391
+ const version = this.options.apiVersion ?? API_VERSION;
1392
+ const baseUrl = this.options.apiBaseUrl ?? API_BASE_URL;
1393
+ const transport = new FetchCommitTransport({ baseUrl, version });
1394
+ const ttl = resolveCommitTtlSeconds(options);
1395
+ const builderOptions = {
1396
+ ...options,
1397
+ ttl
1398
+ };
1399
+ const getAuthToken = () => this.generateJWT(this.id, {
1400
+ permissions: ["git:write"],
1401
+ ttl
1402
+ });
1403
+ return createCommitBuilder({
1404
+ options: builderOptions,
1405
+ getAuthToken,
1406
+ transport
1407
+ });
1408
+ }
384
1409
  };
385
1410
  var GitStorage = class _GitStorage {
386
1411
  static overrides = {};
@@ -401,13 +1426,15 @@ var GitStorage = class _GitStorage {
401
1426
  const resolvedApiBaseUrl = options.apiBaseUrl ?? _GitStorage.overrides.apiBaseUrl ?? API_BASE_URL;
402
1427
  const resolvedApiVersion = options.apiVersion ?? _GitStorage.overrides.apiVersion ?? API_VERSION;
403
1428
  const resolvedStorageBaseUrl = options.storageBaseUrl ?? _GitStorage.overrides.storageBaseUrl ?? STORAGE_BASE_URL;
1429
+ const resolvedDefaultTtl = options.defaultTTL ?? _GitStorage.overrides.defaultTTL;
404
1430
  this.api = getApiInstance(resolvedApiBaseUrl, resolvedApiVersion);
405
1431
  this.options = {
406
1432
  key: options.key,
407
1433
  name: options.name,
408
1434
  apiBaseUrl: resolvedApiBaseUrl,
409
1435
  apiVersion: resolvedApiVersion,
410
- storageBaseUrl: resolvedStorageBaseUrl
1436
+ storageBaseUrl: resolvedStorageBaseUrl,
1437
+ defaultTTL: resolvedDefaultTtl
411
1438
  };
412
1439
  }
413
1440
  static override(options) {
@@ -419,10 +1446,10 @@ var GitStorage = class _GitStorage {
419
1446
  */
420
1447
  async createRepo(options) {
421
1448
  const repoId = options?.id || crypto.randomUUID();
1449
+ const ttl = resolveInvocationTtlSeconds(options, DEFAULT_TOKEN_TTL_SECONDS);
422
1450
  const jwt = await this.generateJWT(repoId, {
423
1451
  permissions: ["repo:write"],
424
- ttl: options?.ttl ?? 1 * 60 * 60
425
- // 1hr in seconds
1452
+ ttl
426
1453
  });
427
1454
  const baseRepoOptions = options?.baseRepo ? {
428
1455
  provider: "github",
@@ -450,7 +1477,7 @@ var GitStorage = class _GitStorage {
450
1477
  async findOne(options) {
451
1478
  const jwt = await this.generateJWT(options.id, {
452
1479
  permissions: ["git:read"],
453
- ttl: 1 * 60 * 60
1480
+ ttl: DEFAULT_TOKEN_TTL_SECONDS
454
1481
  });
455
1482
  const resp = await this.api.get("repo", jwt, { allowedStatus: [404] });
456
1483
  if (resp.status === 404) {
@@ -471,7 +1498,7 @@ var GitStorage = class _GitStorage {
471
1498
  */
472
1499
  async generateJWT(repoId, options) {
473
1500
  const permissions = options?.permissions || ["git:write", "git:read"];
474
- const ttl = options?.ttl || 365 * 24 * 60 * 60;
1501
+ const ttl = resolveInvocationTtlSeconds(options, this.options.defaultTTL ?? 365 * 24 * 60 * 60);
475
1502
  const now = Math.floor(Date.now() / 1e3);
476
1503
  const payload = {
477
1504
  iss: this.options.name,
@@ -490,6 +1517,6 @@ function createClient(options) {
490
1517
  return new GitStorage(options);
491
1518
  }
492
1519
 
493
- export { GitStorage, createClient, parseSignatureHeader, validateWebhook, validateWebhookSignature };
1520
+ export { ApiError, GitStorage, RefUpdateError, createClient, parseSignatureHeader, validateWebhook, validateWebhookSignature };
494
1521
  //# sourceMappingURL=index.js.map
495
1522
  //# sourceMappingURL=index.js.map