@pierre/storage 0.0.5 → 0.0.7
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.cjs +354 -108
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +167 -31
- package/dist/index.d.ts +167 -31
- package/dist/index.js +348 -109
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/src/fetch.ts +71 -0
- package/src/index.ts +164 -144
- package/src/types.ts +97 -9
- package/src/util.ts +62 -0
- package/src/webhook.ts +251 -0
package/dist/index.js
CHANGED
|
@@ -1,16 +1,261 @@
|
|
|
1
1
|
import { importPKCS8, SignJWT } from 'jose';
|
|
2
|
+
import snakecaseKeys from 'snakecase-keys';
|
|
3
|
+
|
|
4
|
+
// src/index.ts
|
|
5
|
+
|
|
6
|
+
// src/fetch.ts
|
|
7
|
+
var ApiFetcher = class {
|
|
8
|
+
constructor(API_BASE_URL2, version) {
|
|
9
|
+
this.API_BASE_URL = API_BASE_URL2;
|
|
10
|
+
this.version = version;
|
|
11
|
+
console.log("api fetcher created", API_BASE_URL2, version);
|
|
12
|
+
}
|
|
13
|
+
getBaseUrl() {
|
|
14
|
+
return `${this.API_BASE_URL}/api/v${this.version}`;
|
|
15
|
+
}
|
|
16
|
+
getRequestUrl(path) {
|
|
17
|
+
if (typeof path === "string") {
|
|
18
|
+
return `${this.getBaseUrl()}/${path}`;
|
|
19
|
+
} else if (path.params) {
|
|
20
|
+
const paramStr = new URLSearchParams(path.params).toString();
|
|
21
|
+
return `${this.getBaseUrl()}/${path.path}${paramStr ? `?${paramStr}` : ""}`;
|
|
22
|
+
} else {
|
|
23
|
+
return `${this.getBaseUrl()}/${path.path}`;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async fetch(path, method, jwt, options) {
|
|
27
|
+
const requestUrl = this.getRequestUrl(path);
|
|
28
|
+
const requestOptions = {
|
|
29
|
+
method,
|
|
30
|
+
headers: {
|
|
31
|
+
Authorization: `Bearer ${jwt}`,
|
|
32
|
+
"Content-Type": "application/json"
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
if (method !== "GET" && typeof path !== "string" && path.body) {
|
|
36
|
+
requestOptions.body = JSON.stringify(path.body);
|
|
37
|
+
}
|
|
38
|
+
const response = await fetch(requestUrl, requestOptions);
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
const allowed = options?.allowedStatus ?? [];
|
|
41
|
+
if (!allowed.includes(response.status)) {
|
|
42
|
+
throw new Error(`Failed to fetch ${method} ${requestUrl}: ${response.statusText}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return response;
|
|
46
|
+
}
|
|
47
|
+
async get(path, jwt, options) {
|
|
48
|
+
return this.fetch(path, "GET", jwt, options);
|
|
49
|
+
}
|
|
50
|
+
async post(path, jwt, options) {
|
|
51
|
+
return this.fetch(path, "POST", jwt, options);
|
|
52
|
+
}
|
|
53
|
+
async put(path, jwt, options) {
|
|
54
|
+
return this.fetch(path, "PUT", jwt, options);
|
|
55
|
+
}
|
|
56
|
+
async delete(path, jwt, options) {
|
|
57
|
+
return this.fetch(path, "DELETE", jwt, options);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// src/util.ts
|
|
62
|
+
function timingSafeEqual(a, b) {
|
|
63
|
+
const bufferA = typeof a === "string" ? new TextEncoder().encode(a) : a;
|
|
64
|
+
const bufferB = typeof b === "string" ? new TextEncoder().encode(b) : b;
|
|
65
|
+
if (bufferA.length !== bufferB.length) return false;
|
|
66
|
+
let result = 0;
|
|
67
|
+
for (let i = 0; i < bufferA.length; i++) {
|
|
68
|
+
result |= bufferA[i] ^ bufferB[i];
|
|
69
|
+
}
|
|
70
|
+
return result === 0;
|
|
71
|
+
}
|
|
72
|
+
async function getEnvironmentCrypto() {
|
|
73
|
+
if (!globalThis.crypto) {
|
|
74
|
+
const { webcrypto } = await import('crypto');
|
|
75
|
+
return webcrypto;
|
|
76
|
+
}
|
|
77
|
+
return globalThis.crypto;
|
|
78
|
+
}
|
|
79
|
+
async function createHmac(algorithm, secret, data) {
|
|
80
|
+
if (!secret || secret.length === 0) {
|
|
81
|
+
throw new Error("Secret is required");
|
|
82
|
+
}
|
|
83
|
+
const crypto2 = await getEnvironmentCrypto();
|
|
84
|
+
const encoder = new TextEncoder();
|
|
85
|
+
const key = await crypto2.subtle.importKey(
|
|
86
|
+
"raw",
|
|
87
|
+
encoder.encode(secret),
|
|
88
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
89
|
+
false,
|
|
90
|
+
["sign"]
|
|
91
|
+
);
|
|
92
|
+
const signature = await crypto2.subtle.sign("HMAC", key, encoder.encode(data));
|
|
93
|
+
return Array.from(new Uint8Array(signature)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/webhook.ts
|
|
97
|
+
var DEFAULT_MAX_AGE_SECONDS = 300;
|
|
98
|
+
function parseSignatureHeader(header) {
|
|
99
|
+
if (!header || typeof header !== "string") {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
let timestamp = "";
|
|
103
|
+
let signature = "";
|
|
104
|
+
const elements = header.split(",");
|
|
105
|
+
for (const element of elements) {
|
|
106
|
+
const trimmedElement = element.trim();
|
|
107
|
+
const parts = trimmedElement.split("=", 2);
|
|
108
|
+
if (parts.length !== 2) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const [key, value] = parts;
|
|
112
|
+
switch (key) {
|
|
113
|
+
case "t":
|
|
114
|
+
timestamp = value;
|
|
115
|
+
break;
|
|
116
|
+
case "sha256":
|
|
117
|
+
signature = value;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (!timestamp || !signature) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
return { timestamp, signature };
|
|
125
|
+
}
|
|
126
|
+
async function validateWebhookSignature(payload, signatureHeader, secret, options = {}) {
|
|
127
|
+
if (!secret || secret.length === 0) {
|
|
128
|
+
return {
|
|
129
|
+
valid: false,
|
|
130
|
+
error: "Empty secret is not allowed"
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const parsed = parseSignatureHeader(signatureHeader);
|
|
134
|
+
if (!parsed) {
|
|
135
|
+
return {
|
|
136
|
+
valid: false,
|
|
137
|
+
error: "Invalid signature header format"
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const timestamp = Number.parseInt(parsed.timestamp, 10);
|
|
141
|
+
if (isNaN(timestamp)) {
|
|
142
|
+
return {
|
|
143
|
+
valid: false,
|
|
144
|
+
error: "Invalid timestamp in signature"
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
const maxAge = options.maxAgeSeconds ?? DEFAULT_MAX_AGE_SECONDS;
|
|
148
|
+
if (maxAge > 0) {
|
|
149
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
150
|
+
const age = now - timestamp;
|
|
151
|
+
if (age > maxAge) {
|
|
152
|
+
return {
|
|
153
|
+
valid: false,
|
|
154
|
+
error: `Webhook timestamp too old (${age} seconds)`,
|
|
155
|
+
timestamp
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (age < -60) {
|
|
159
|
+
return {
|
|
160
|
+
valid: false,
|
|
161
|
+
error: "Webhook timestamp is in the future",
|
|
162
|
+
timestamp
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const payloadStr = typeof payload === "string" ? payload : payload.toString("utf8");
|
|
167
|
+
const signedData = `${parsed.timestamp}.${payloadStr}`;
|
|
168
|
+
const expectedSignature = await createHmac("sha256", secret, signedData);
|
|
169
|
+
const expectedBuffer = Buffer.from(expectedSignature);
|
|
170
|
+
const actualBuffer = Buffer.from(parsed.signature);
|
|
171
|
+
if (expectedBuffer.length !== actualBuffer.length) {
|
|
172
|
+
return {
|
|
173
|
+
valid: false,
|
|
174
|
+
error: "Invalid signature",
|
|
175
|
+
timestamp
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const signaturesMatch = timingSafeEqual(expectedBuffer, actualBuffer);
|
|
179
|
+
if (!signaturesMatch) {
|
|
180
|
+
return {
|
|
181
|
+
valid: false,
|
|
182
|
+
error: "Invalid signature",
|
|
183
|
+
timestamp
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
valid: true,
|
|
188
|
+
timestamp
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
async function validateWebhook(payload, headers, secret, options = {}) {
|
|
192
|
+
const signatureHeader = headers["x-pierre-signature"] || headers["X-Pierre-Signature"];
|
|
193
|
+
if (!signatureHeader || Array.isArray(signatureHeader)) {
|
|
194
|
+
return {
|
|
195
|
+
valid: false,
|
|
196
|
+
error: "Missing or invalid X-Pierre-Signature header"
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const eventType = headers["x-pierre-event"] || headers["X-Pierre-Event"];
|
|
200
|
+
if (!eventType || Array.isArray(eventType)) {
|
|
201
|
+
return {
|
|
202
|
+
valid: false,
|
|
203
|
+
error: "Missing or invalid X-Pierre-Event header"
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const validationResult = await validateWebhookSignature(
|
|
207
|
+
payload,
|
|
208
|
+
signatureHeader,
|
|
209
|
+
secret,
|
|
210
|
+
options
|
|
211
|
+
);
|
|
212
|
+
if (!validationResult.valid) {
|
|
213
|
+
return validationResult;
|
|
214
|
+
}
|
|
215
|
+
const payloadStr = typeof payload === "string" ? payload : payload.toString("utf8");
|
|
216
|
+
let parsedPayload;
|
|
217
|
+
try {
|
|
218
|
+
parsedPayload = JSON.parse(payloadStr);
|
|
219
|
+
} catch {
|
|
220
|
+
return {
|
|
221
|
+
valid: false,
|
|
222
|
+
error: "Invalid JSON payload",
|
|
223
|
+
timestamp: validationResult.timestamp
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
valid: true,
|
|
228
|
+
eventType,
|
|
229
|
+
timestamp: validationResult.timestamp,
|
|
230
|
+
payload: parsedPayload
|
|
231
|
+
};
|
|
232
|
+
}
|
|
2
233
|
|
|
3
234
|
// src/index.ts
|
|
4
235
|
var API_BASE_URL = "https://api.git.storage";
|
|
5
236
|
var STORAGE_BASE_URL = "git.storage";
|
|
237
|
+
var API_VERSION = 1;
|
|
238
|
+
var apiInstanceMap = /* @__PURE__ */ new Map();
|
|
239
|
+
function getApiInstance(baseUrl, version) {
|
|
240
|
+
if (!apiInstanceMap.has(`${baseUrl}--${version}`)) {
|
|
241
|
+
apiInstanceMap.set(`${baseUrl}--${version}`, new ApiFetcher(baseUrl, version));
|
|
242
|
+
}
|
|
243
|
+
return apiInstanceMap.get(`${baseUrl}--${version}`);
|
|
244
|
+
}
|
|
6
245
|
var RepoImpl = class {
|
|
7
246
|
constructor(id, options, generateJWT) {
|
|
8
247
|
this.id = id;
|
|
9
248
|
this.options = options;
|
|
10
249
|
this.generateJWT = generateJWT;
|
|
250
|
+
this.api = getApiInstance(
|
|
251
|
+
this.options.apiBaseUrl ?? API_BASE_URL,
|
|
252
|
+
this.options.apiVersion ?? API_VERSION
|
|
253
|
+
);
|
|
11
254
|
}
|
|
255
|
+
api;
|
|
12
256
|
async getRemoteURL(urlOptions) {
|
|
13
|
-
const
|
|
257
|
+
const storageBaseUrl = this.options.storageBaseUrl ?? STORAGE_BASE_URL;
|
|
258
|
+
const url = new URL(`https://${this.options.name}.${storageBaseUrl}/${this.id}.git`);
|
|
14
259
|
url.username = `t`;
|
|
15
260
|
url.password = await this.generateJWT(this.id, urlOptions);
|
|
16
261
|
return url.toString();
|
|
@@ -18,161 +263,130 @@ var RepoImpl = class {
|
|
|
18
263
|
async getFile(options) {
|
|
19
264
|
const jwt = await this.generateJWT(this.id, {
|
|
20
265
|
permissions: ["git:read"],
|
|
21
|
-
ttl: 1 * 60 * 60
|
|
266
|
+
ttl: options?.ttl ?? 1 * 60 * 60
|
|
22
267
|
// 1hr in seconds
|
|
23
268
|
});
|
|
24
|
-
const
|
|
25
|
-
|
|
269
|
+
const params = {
|
|
270
|
+
path: options.path
|
|
271
|
+
};
|
|
26
272
|
if (options.ref) {
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
const response = await fetch(url.toString(), {
|
|
30
|
-
method: "GET",
|
|
31
|
-
headers: {
|
|
32
|
-
Authorization: `Bearer ${jwt}`
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
if (!response.ok) {
|
|
36
|
-
throw new Error(`Failed to get file: ${response.statusText}`);
|
|
273
|
+
params.ref = options.ref;
|
|
37
274
|
}
|
|
275
|
+
const response = await this.api.get({ path: "repos/file", params }, jwt);
|
|
38
276
|
return await response.json();
|
|
39
277
|
}
|
|
40
278
|
async listFiles(options) {
|
|
41
279
|
const jwt = await this.generateJWT(this.id, {
|
|
42
280
|
permissions: ["git:read"],
|
|
43
|
-
ttl: 1 * 60 * 60
|
|
281
|
+
ttl: options?.ttl ?? 1 * 60 * 60
|
|
44
282
|
// 1hr in seconds
|
|
45
283
|
});
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
url.searchParams.set("ref", options.ref);
|
|
49
|
-
}
|
|
50
|
-
const response = await fetch(url.toString(), {
|
|
51
|
-
method: "GET",
|
|
52
|
-
headers: {
|
|
53
|
-
Authorization: `Bearer ${jwt}`
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
if (!response.ok) {
|
|
57
|
-
throw new Error(`Failed to list files: ${response.statusText}`);
|
|
58
|
-
}
|
|
284
|
+
const params = options?.ref ? { ref: options.ref } : void 0;
|
|
285
|
+
const response = await this.api.get({ path: "repos/files", params }, jwt);
|
|
59
286
|
return await response.json();
|
|
60
287
|
}
|
|
61
288
|
async listBranches(options) {
|
|
62
289
|
const jwt = await this.generateJWT(this.id, {
|
|
63
290
|
permissions: ["git:read"],
|
|
64
|
-
ttl: 1 * 60 * 60
|
|
291
|
+
ttl: options?.ttl ?? 1 * 60 * 60
|
|
65
292
|
// 1hr in seconds
|
|
66
293
|
});
|
|
67
|
-
|
|
68
|
-
if (options?.cursor) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
method: "GET",
|
|
76
|
-
headers: {
|
|
77
|
-
Authorization: `Bearer ${jwt}`
|
|
294
|
+
let params;
|
|
295
|
+
if (options?.cursor || !options?.limit) {
|
|
296
|
+
params = {};
|
|
297
|
+
if (options?.cursor) {
|
|
298
|
+
params.cursor = options.cursor;
|
|
299
|
+
}
|
|
300
|
+
if (typeof options?.limit == "number") {
|
|
301
|
+
params.limit = options.limit.toString();
|
|
78
302
|
}
|
|
79
|
-
});
|
|
80
|
-
if (!response.ok) {
|
|
81
|
-
throw new Error(`Failed to list branches: ${response.statusText}`);
|
|
82
303
|
}
|
|
304
|
+
const response = await this.api.get({ path: "repos/branches", params }, jwt);
|
|
83
305
|
return await response.json();
|
|
84
306
|
}
|
|
85
307
|
async listCommits(options) {
|
|
86
308
|
const jwt = await this.generateJWT(this.id, {
|
|
87
309
|
permissions: ["git:read"],
|
|
88
|
-
ttl: 1 * 60 * 60
|
|
310
|
+
ttl: options?.ttl ?? 1 * 60 * 60
|
|
89
311
|
// 1hr in seconds
|
|
90
312
|
});
|
|
91
|
-
|
|
92
|
-
if (options?.branch) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
method: "GET",
|
|
103
|
-
headers: {
|
|
104
|
-
Authorization: `Bearer ${jwt}`
|
|
313
|
+
let params;
|
|
314
|
+
if (options?.branch || options?.cursor || options?.limit) {
|
|
315
|
+
params = {};
|
|
316
|
+
if (options?.branch) {
|
|
317
|
+
params.branch = options.branch;
|
|
318
|
+
}
|
|
319
|
+
if (options?.cursor) {
|
|
320
|
+
params.cursor = options.cursor;
|
|
321
|
+
}
|
|
322
|
+
if (typeof options?.limit == "number") {
|
|
323
|
+
params.limit = options.limit.toString();
|
|
105
324
|
}
|
|
106
|
-
});
|
|
107
|
-
if (!response.ok) {
|
|
108
|
-
throw new Error(`Failed to list commits: ${response.statusText}`);
|
|
109
325
|
}
|
|
326
|
+
const response = await this.api.get({ path: "repos/commits", params }, jwt);
|
|
110
327
|
return await response.json();
|
|
111
328
|
}
|
|
112
329
|
async getBranchDiff(options) {
|
|
113
330
|
const jwt = await this.generateJWT(this.id, {
|
|
114
331
|
permissions: ["git:read"],
|
|
115
|
-
ttl: 1 * 60 * 60
|
|
332
|
+
ttl: options?.ttl ?? 1 * 60 * 60
|
|
116
333
|
// 1hr in seconds
|
|
117
334
|
});
|
|
118
|
-
const
|
|
119
|
-
|
|
335
|
+
const params = {
|
|
336
|
+
branch: options.branch
|
|
337
|
+
};
|
|
120
338
|
if (options.base) {
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
const response = await fetch(url.toString(), {
|
|
124
|
-
method: "GET",
|
|
125
|
-
headers: {
|
|
126
|
-
Authorization: `Bearer ${jwt}`
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
if (!response.ok) {
|
|
130
|
-
throw new Error(`Failed to get branch diff: ${response.statusText}`);
|
|
339
|
+
params.base = options.base;
|
|
131
340
|
}
|
|
341
|
+
const response = await this.api.get({ path: "repos/branches/diff", params }, jwt);
|
|
132
342
|
return await response.json();
|
|
133
343
|
}
|
|
134
344
|
async getCommitDiff(options) {
|
|
135
345
|
const jwt = await this.generateJWT(this.id, {
|
|
136
346
|
permissions: ["git:read"],
|
|
137
|
-
ttl: 1 * 60 * 60
|
|
347
|
+
ttl: options?.ttl ?? 1 * 60 * 60
|
|
138
348
|
// 1hr in seconds
|
|
139
349
|
});
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
headers: {
|
|
145
|
-
Authorization: `Bearer ${jwt}`
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
if (!response.ok) {
|
|
149
|
-
throw new Error(`Failed to get commit diff: ${response.statusText}`);
|
|
150
|
-
}
|
|
350
|
+
const params = {
|
|
351
|
+
sha: options.sha
|
|
352
|
+
};
|
|
353
|
+
const response = await this.api.get({ path: "repos/diff", params }, jwt);
|
|
151
354
|
return await response.json();
|
|
152
355
|
}
|
|
153
356
|
async getCommit(options) {
|
|
154
357
|
const jwt = await this.generateJWT(this.id, {
|
|
155
358
|
permissions: ["git:read"],
|
|
156
|
-
ttl: 1 * 60 * 60
|
|
359
|
+
ttl: options?.ttl ?? 1 * 60 * 60
|
|
157
360
|
// 1hr in seconds
|
|
158
361
|
});
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
362
|
+
const params = {
|
|
363
|
+
repo: this.id,
|
|
364
|
+
sha: options.sha
|
|
365
|
+
};
|
|
366
|
+
const response = await this.api.get({ path: "commit", params }, jwt);
|
|
367
|
+
return await response.json();
|
|
368
|
+
}
|
|
369
|
+
async repull(options) {
|
|
370
|
+
const jwt = await this.generateJWT(this.id, {
|
|
371
|
+
permissions: ["git:write"],
|
|
372
|
+
ttl: options?.ttl ?? 1 * 60 * 60
|
|
373
|
+
// 1hr in seconds
|
|
167
374
|
});
|
|
168
|
-
|
|
169
|
-
|
|
375
|
+
const body = {};
|
|
376
|
+
if (options.ref) {
|
|
377
|
+
body.ref = options.ref;
|
|
170
378
|
}
|
|
171
|
-
|
|
379
|
+
const response = await this.api.post({ path: "repos/repull", body }, jwt);
|
|
380
|
+
if (response.status !== 202) {
|
|
381
|
+
throw new Error(`Repull failed: ${response.status} ${await response.text()}`);
|
|
382
|
+
}
|
|
383
|
+
return;
|
|
172
384
|
}
|
|
173
385
|
};
|
|
174
|
-
var GitStorage = class {
|
|
386
|
+
var GitStorage = class _GitStorage {
|
|
387
|
+
static overrides = {};
|
|
175
388
|
options;
|
|
389
|
+
api;
|
|
176
390
|
constructor(options) {
|
|
177
391
|
if (!options || options.name === void 0 || options.key === void 0 || options.name === null || options.key === null) {
|
|
178
392
|
throw new Error(
|
|
@@ -185,11 +399,21 @@ var GitStorage = class {
|
|
|
185
399
|
if (typeof options.key !== "string" || options.key.trim() === "") {
|
|
186
400
|
throw new Error("GitStorage key must be a non-empty string.");
|
|
187
401
|
}
|
|
402
|
+
const resolvedApiBaseUrl = options.apiBaseUrl ?? _GitStorage.overrides.apiBaseUrl ?? API_BASE_URL;
|
|
403
|
+
const resolvedApiVersion = options.apiVersion ?? _GitStorage.overrides.apiVersion ?? API_VERSION;
|
|
404
|
+
const resolvedStorageBaseUrl = options.storageBaseUrl ?? _GitStorage.overrides.storageBaseUrl ?? STORAGE_BASE_URL;
|
|
405
|
+
this.api = getApiInstance(resolvedApiBaseUrl, resolvedApiVersion);
|
|
188
406
|
this.options = {
|
|
189
407
|
key: options.key,
|
|
190
|
-
name: options.name
|
|
408
|
+
name: options.name,
|
|
409
|
+
apiBaseUrl: resolvedApiBaseUrl,
|
|
410
|
+
apiVersion: resolvedApiVersion,
|
|
411
|
+
storageBaseUrl: resolvedStorageBaseUrl
|
|
191
412
|
};
|
|
192
413
|
}
|
|
414
|
+
static override(options) {
|
|
415
|
+
this.overrides = Object.assign({}, this.overrides, options);
|
|
416
|
+
}
|
|
193
417
|
/**
|
|
194
418
|
* Create a new repository
|
|
195
419
|
* @returns The created repository
|
|
@@ -198,17 +422,24 @@ var GitStorage = class {
|
|
|
198
422
|
const repoId = options?.id || crypto.randomUUID();
|
|
199
423
|
const jwt = await this.generateJWT(repoId, {
|
|
200
424
|
permissions: ["repo:write"],
|
|
201
|
-
ttl: 1 * 60 * 60
|
|
425
|
+
ttl: options?.ttl ?? 1 * 60 * 60
|
|
202
426
|
// 1hr in seconds
|
|
203
427
|
});
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
428
|
+
const baseRepoOptions = options?.baseRepo ? {
|
|
429
|
+
provider: "github",
|
|
430
|
+
...snakecaseKeys(options.baseRepo)
|
|
431
|
+
} : null;
|
|
432
|
+
const defaultBranch = options?.defaultBranch ?? "main";
|
|
433
|
+
const createRepoPath = baseRepoOptions || defaultBranch ? {
|
|
434
|
+
path: "repos",
|
|
435
|
+
body: {
|
|
436
|
+
...baseRepoOptions && { base_repo: baseRepoOptions },
|
|
437
|
+
default_branch: defaultBranch
|
|
208
438
|
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
|
|
439
|
+
} : "repos";
|
|
440
|
+
const resp = await this.api.post(createRepoPath, jwt, { allowedStatus: [409] });
|
|
441
|
+
if (resp.status === 409) {
|
|
442
|
+
throw new Error("Repository already exists");
|
|
212
443
|
}
|
|
213
444
|
return new RepoImpl(repoId, this.options, this.generateJWT.bind(this));
|
|
214
445
|
}
|
|
@@ -218,6 +449,14 @@ var GitStorage = class {
|
|
|
218
449
|
* @returns The found repository
|
|
219
450
|
*/
|
|
220
451
|
async findOne(options) {
|
|
452
|
+
const jwt = await this.generateJWT(options.id, {
|
|
453
|
+
permissions: ["git:read"],
|
|
454
|
+
ttl: 1 * 60 * 60
|
|
455
|
+
});
|
|
456
|
+
const resp = await this.api.get("repo", jwt, { allowedStatus: [404] });
|
|
457
|
+
if (resp.status === 404) {
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
221
460
|
return new RepoImpl(options.id, this.options, this.generateJWT.bind(this));
|
|
222
461
|
}
|
|
223
462
|
/**
|
|
@@ -252,6 +491,6 @@ function createClient(options) {
|
|
|
252
491
|
return new GitStorage(options);
|
|
253
492
|
}
|
|
254
493
|
|
|
255
|
-
export { GitStorage, createClient };
|
|
494
|
+
export { GitStorage, createClient, parseSignatureHeader, validateWebhook, validateWebhookSignature };
|
|
256
495
|
//# sourceMappingURL=index.js.map
|
|
257
496
|
//# sourceMappingURL=index.js.map
|