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