@pierre/storage 0.0.3 → 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/README.md +170 -0
- package/dist/index.cjs +424 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +271 -8
- package/dist/index.d.ts +271 -8
- package/dist/index.js +418 -29
- package/dist/index.js.map +1 -1
- package/package.json +37 -37
- package/src/fetch.ts +71 -0
- package/src/index.ts +273 -53
- package/src/types.ts +237 -2
- package/src/util.ts +62 -0
- package/src/webhook.ts +251 -0
package/src/fetch.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ValidAPIVersion, ValidMethod, ValidPath } from './types';
|
|
2
|
+
|
|
3
|
+
interface RequestOptions {
|
|
4
|
+
allowedStatus?: number[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export class ApiFetcher {
|
|
8
|
+
constructor(
|
|
9
|
+
private readonly API_BASE_URL: string,
|
|
10
|
+
private readonly version: ValidAPIVersion,
|
|
11
|
+
) {
|
|
12
|
+
console.log('api fetcher created', API_BASE_URL, version);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private getBaseUrl() {
|
|
16
|
+
return `${this.API_BASE_URL}/api/v${this.version}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private getRequestUrl(path: ValidPath) {
|
|
20
|
+
if (typeof path === 'string') {
|
|
21
|
+
return `${this.getBaseUrl()}/${path}`;
|
|
22
|
+
} else if (path.params) {
|
|
23
|
+
const paramStr = new URLSearchParams(path.params).toString();
|
|
24
|
+
return `${this.getBaseUrl()}/${path.path}${paramStr ? `?${paramStr}` : ''}`;
|
|
25
|
+
} else {
|
|
26
|
+
return `${this.getBaseUrl()}/${path.path}`;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private async fetch(path: ValidPath, method: ValidMethod, jwt: string, options?: RequestOptions) {
|
|
31
|
+
const requestUrl = this.getRequestUrl(path);
|
|
32
|
+
|
|
33
|
+
const requestOptions: RequestInit = {
|
|
34
|
+
method,
|
|
35
|
+
headers: {
|
|
36
|
+
Authorization: `Bearer ${jwt}`,
|
|
37
|
+
'Content-Type': 'application/json',
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
if (method !== 'GET' && typeof path !== 'string' && path.body) {
|
|
42
|
+
requestOptions.body = JSON.stringify(path.body);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const response = await fetch(requestUrl, requestOptions);
|
|
46
|
+
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
const allowed = options?.allowedStatus ?? [];
|
|
49
|
+
if (!allowed.includes(response.status)) {
|
|
50
|
+
throw new Error(`Failed to fetch ${method} ${requestUrl}: ${response.statusText}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return response;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async get(path: ValidPath, jwt: string, options?: RequestOptions) {
|
|
57
|
+
return this.fetch(path, 'GET', jwt, options);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async post(path: ValidPath, jwt: string, options?: RequestOptions) {
|
|
61
|
+
return this.fetch(path, 'POST', jwt, options);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async put(path: ValidPath, jwt: string, options?: RequestOptions) {
|
|
65
|
+
return this.fetch(path, 'PUT', jwt, options);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async delete(path: ValidPath, jwt: string, options?: RequestOptions) {
|
|
69
|
+
return this.fetch(path, 'DELETE', jwt, options);
|
|
70
|
+
}
|
|
71
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,50 +5,248 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { importPKCS8, SignJWT } from 'jose';
|
|
8
|
+
import snakecaseKeys from 'snakecase-keys';
|
|
9
|
+
import { ApiFetcher } from './fetch';
|
|
10
|
+
import type {
|
|
11
|
+
CreateRepoOptions,
|
|
12
|
+
FindOneOptions,
|
|
13
|
+
GetBranchDiffOptions,
|
|
14
|
+
GetBranchDiffResponse,
|
|
15
|
+
GetCommitDiffOptions,
|
|
16
|
+
GetCommitDiffResponse,
|
|
17
|
+
GetCommitOptions,
|
|
18
|
+
GetCommitResponse,
|
|
19
|
+
GetFileOptions,
|
|
20
|
+
GetFileResponse,
|
|
21
|
+
GetRemoteURLOptions,
|
|
22
|
+
GitStorageOptions,
|
|
23
|
+
ListBranchesOptions,
|
|
24
|
+
ListBranchesResponse,
|
|
25
|
+
ListCommitsOptions,
|
|
26
|
+
ListCommitsResponse,
|
|
27
|
+
ListFilesOptions,
|
|
28
|
+
ListFilesResponse,
|
|
29
|
+
OverrideableGitStorageOptions,
|
|
30
|
+
Repo,
|
|
31
|
+
RepullOptions,
|
|
32
|
+
ValidAPIVersion,
|
|
33
|
+
} from './types';
|
|
8
34
|
|
|
9
35
|
/**
|
|
10
36
|
* Type definitions for Pierre Git Storage SDK
|
|
11
37
|
*/
|
|
12
38
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
name: string;
|
|
16
|
-
}
|
|
39
|
+
// Import additional types from types.ts
|
|
40
|
+
export * from './types';
|
|
17
41
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
ttl?: number;
|
|
21
|
-
}
|
|
42
|
+
// Export webhook validation utilities
|
|
43
|
+
export { parseSignatureHeader, validateWebhook, validateWebhookSignature } from './webhook';
|
|
22
44
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
45
|
+
/**
|
|
46
|
+
* Git Storage API
|
|
47
|
+
*/
|
|
27
48
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
49
|
+
declare const __STORAGE_BASE_URL__: string;
|
|
50
|
+
declare const __API_BASE_URL__: string;
|
|
31
51
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
52
|
+
const API_BASE_URL = __API_BASE_URL__;
|
|
53
|
+
const STORAGE_BASE_URL = __STORAGE_BASE_URL__;
|
|
54
|
+
const API_VERSION: ValidAPIVersion = 1;
|
|
35
55
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
56
|
+
const apiInstanceMap = new Map<string, ApiFetcher>();
|
|
57
|
+
|
|
58
|
+
function getApiInstance(baseUrl: string, version: ValidAPIVersion) {
|
|
59
|
+
if (!apiInstanceMap.has(`${baseUrl}--${version}`)) {
|
|
60
|
+
apiInstanceMap.set(`${baseUrl}--${version}`, new ApiFetcher(baseUrl, version));
|
|
61
|
+
}
|
|
62
|
+
return apiInstanceMap.get(`${baseUrl}--${version}`)!;
|
|
39
63
|
}
|
|
40
64
|
|
|
41
65
|
/**
|
|
42
|
-
*
|
|
66
|
+
* Implementation of the Repo interface
|
|
43
67
|
*/
|
|
44
|
-
|
|
45
|
-
|
|
68
|
+
class RepoImpl implements Repo {
|
|
69
|
+
private readonly api: ApiFetcher;
|
|
46
70
|
|
|
47
|
-
|
|
48
|
-
|
|
71
|
+
constructor(
|
|
72
|
+
public readonly id: string,
|
|
73
|
+
private readonly options: GitStorageOptions,
|
|
74
|
+
private readonly generateJWT: (
|
|
75
|
+
repoId: string,
|
|
76
|
+
options?: GetRemoteURLOptions,
|
|
77
|
+
) => Promise<string>,
|
|
78
|
+
) {
|
|
79
|
+
this.api = getApiInstance(
|
|
80
|
+
this.options.apiBaseUrl ?? API_BASE_URL,
|
|
81
|
+
this.options.apiVersion ?? API_VERSION,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async getRemoteURL(urlOptions?: GetRemoteURLOptions): Promise<string> {
|
|
86
|
+
const storageBaseUrl = this.options.storageBaseUrl ?? STORAGE_BASE_URL;
|
|
87
|
+
const url = new URL(`https://${this.options.name}.${storageBaseUrl}/${this.id}.git`);
|
|
88
|
+
url.username = `t`;
|
|
89
|
+
url.password = await this.generateJWT(this.id, urlOptions);
|
|
90
|
+
return url.toString();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async getFile(options: GetFileOptions): Promise<GetFileResponse> {
|
|
94
|
+
const jwt = await this.generateJWT(this.id, {
|
|
95
|
+
permissions: ['git:read'],
|
|
96
|
+
ttl: options?.ttl ?? 1 * 60 * 60, // 1hr in seconds
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const params: Record<string, string> = {
|
|
100
|
+
path: options.path,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
if (options.ref) {
|
|
104
|
+
params.ref = options.ref;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const response = await this.api.get({ path: 'repos/file', params }, jwt);
|
|
108
|
+
|
|
109
|
+
return (await response.json()) as GetFileResponse;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async listFiles(options?: ListFilesOptions): Promise<ListFilesResponse> {
|
|
113
|
+
const jwt = await this.generateJWT(this.id, {
|
|
114
|
+
permissions: ['git:read'],
|
|
115
|
+
ttl: options?.ttl ?? 1 * 60 * 60, // 1hr in seconds
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const params: Record<string, string> | undefined = options?.ref
|
|
119
|
+
? { ref: options.ref }
|
|
120
|
+
: undefined;
|
|
121
|
+
const response = await this.api.get({ path: 'repos/files', params }, jwt);
|
|
122
|
+
|
|
123
|
+
return (await response.json()) as ListFilesResponse;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async listBranches(options?: ListBranchesOptions): Promise<ListBranchesResponse> {
|
|
127
|
+
const jwt = await this.generateJWT(this.id, {
|
|
128
|
+
permissions: ['git:read'],
|
|
129
|
+
ttl: options?.ttl ?? 1 * 60 * 60, // 1hr in seconds
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
let params: Record<string, string> | undefined;
|
|
133
|
+
|
|
134
|
+
if (options?.cursor || !options?.limit) {
|
|
135
|
+
params = {};
|
|
136
|
+
if (options?.cursor) {
|
|
137
|
+
params.cursor = options.cursor;
|
|
138
|
+
}
|
|
139
|
+
if (typeof options?.limit == 'number') {
|
|
140
|
+
params.limit = options.limit.toString();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const response = await this.api.get({ path: 'repos/branches', params }, jwt);
|
|
145
|
+
|
|
146
|
+
return (await response.json()) as ListBranchesResponse;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async listCommits(options?: ListCommitsOptions): Promise<ListCommitsResponse> {
|
|
150
|
+
const jwt = await this.generateJWT(this.id, {
|
|
151
|
+
permissions: ['git:read'],
|
|
152
|
+
ttl: options?.ttl ?? 1 * 60 * 60, // 1hr in seconds
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
let params: Record<string, string> | undefined;
|
|
156
|
+
|
|
157
|
+
if (options?.branch || options?.cursor || options?.limit) {
|
|
158
|
+
params = {};
|
|
159
|
+
if (options?.branch) {
|
|
160
|
+
params.branch = options.branch;
|
|
161
|
+
}
|
|
162
|
+
if (options?.cursor) {
|
|
163
|
+
params.cursor = options.cursor;
|
|
164
|
+
}
|
|
165
|
+
if (typeof options?.limit == 'number') {
|
|
166
|
+
params.limit = options.limit.toString();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const response = await this.api.get({ path: 'repos/commits', params }, jwt);
|
|
171
|
+
|
|
172
|
+
return (await response.json()) as ListCommitsResponse;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async getBranchDiff(options: GetBranchDiffOptions): Promise<GetBranchDiffResponse> {
|
|
176
|
+
const jwt = await this.generateJWT(this.id, {
|
|
177
|
+
permissions: ['git:read'],
|
|
178
|
+
ttl: options?.ttl ?? 1 * 60 * 60, // 1hr in seconds
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const params: Record<string, string> = {
|
|
182
|
+
branch: options.branch,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
if (options.base) {
|
|
186
|
+
params.base = options.base;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const response = await this.api.get({ path: 'repos/branches/diff', params }, jwt);
|
|
190
|
+
|
|
191
|
+
return (await response.json()) as GetBranchDiffResponse;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async getCommitDiff(options: GetCommitDiffOptions): Promise<GetCommitDiffResponse> {
|
|
195
|
+
const jwt = await this.generateJWT(this.id, {
|
|
196
|
+
permissions: ['git:read'],
|
|
197
|
+
ttl: options?.ttl ?? 1 * 60 * 60, // 1hr in seconds
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const params: Record<string, string> = {
|
|
201
|
+
sha: options.sha,
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const response = await this.api.get({ path: 'repos/diff', params }, jwt);
|
|
205
|
+
|
|
206
|
+
return (await response.json()) as GetCommitDiffResponse;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async getCommit(options: GetCommitOptions): Promise<GetCommitResponse> {
|
|
210
|
+
const jwt = await this.generateJWT(this.id, {
|
|
211
|
+
permissions: ['git:read'],
|
|
212
|
+
ttl: options?.ttl ?? 1 * 60 * 60, // 1hr in seconds
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const params: Record<string, string> = {
|
|
216
|
+
repo: this.id,
|
|
217
|
+
sha: options.sha,
|
|
218
|
+
};
|
|
219
|
+
const response = await this.api.get({ path: 'commit', params }, jwt);
|
|
220
|
+
|
|
221
|
+
return (await response.json()) as GetCommitResponse;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async repull(options: RepullOptions): Promise<void> {
|
|
225
|
+
const jwt = await this.generateJWT(this.id, {
|
|
226
|
+
permissions: ['git:write'],
|
|
227
|
+
ttl: options?.ttl ?? 1 * 60 * 60, // 1hr in seconds
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const body: Record<string, string> = {};
|
|
231
|
+
|
|
232
|
+
if (options.ref) {
|
|
233
|
+
body.ref = options.ref;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const response = await this.api.post({ path: 'repos/repull', body }, jwt);
|
|
237
|
+
|
|
238
|
+
if (response.status !== 202) {
|
|
239
|
+
throw new Error(`Repull failed: ${response.status} ${await response.text()}`);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
49
245
|
|
|
50
246
|
export class GitStorage {
|
|
247
|
+
private static overrides: OverrideableGitStorageOptions = {};
|
|
51
248
|
private options: GitStorageOptions;
|
|
249
|
+
private api: ApiFetcher;
|
|
52
250
|
|
|
53
251
|
constructor(options: GitStorageOptions) {
|
|
54
252
|
if (
|
|
@@ -71,12 +269,27 @@ export class GitStorage {
|
|
|
71
269
|
throw new Error('GitStorage key must be a non-empty string.');
|
|
72
270
|
}
|
|
73
271
|
|
|
272
|
+
const resolvedApiBaseUrl =
|
|
273
|
+
options.apiBaseUrl ?? GitStorage.overrides.apiBaseUrl ?? API_BASE_URL;
|
|
274
|
+
const resolvedApiVersion = options.apiVersion ?? GitStorage.overrides.apiVersion ?? API_VERSION;
|
|
275
|
+
const resolvedStorageBaseUrl =
|
|
276
|
+
options.storageBaseUrl ?? GitStorage.overrides.storageBaseUrl ?? STORAGE_BASE_URL;
|
|
277
|
+
|
|
278
|
+
this.api = getApiInstance(resolvedApiBaseUrl, resolvedApiVersion);
|
|
279
|
+
|
|
74
280
|
this.options = {
|
|
75
281
|
key: options.key,
|
|
76
282
|
name: options.name,
|
|
283
|
+
apiBaseUrl: resolvedApiBaseUrl,
|
|
284
|
+
apiVersion: resolvedApiVersion,
|
|
285
|
+
storageBaseUrl: resolvedStorageBaseUrl,
|
|
77
286
|
};
|
|
78
287
|
}
|
|
79
288
|
|
|
289
|
+
static override(options: OverrideableGitStorageOptions): void {
|
|
290
|
+
this.overrides = Object.assign({}, this.overrides, options);
|
|
291
|
+
}
|
|
292
|
+
|
|
80
293
|
/**
|
|
81
294
|
* Create a new repository
|
|
82
295
|
* @returns The created repository
|
|
@@ -86,29 +299,32 @@ export class GitStorage {
|
|
|
86
299
|
|
|
87
300
|
const jwt = await this.generateJWT(repoId, {
|
|
88
301
|
permissions: ['repo:write'],
|
|
89
|
-
ttl: 1 * 60 * 60, // 1hr in seconds
|
|
302
|
+
ttl: options?.ttl ?? 1 * 60 * 60, // 1hr in seconds
|
|
90
303
|
});
|
|
91
304
|
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
305
|
+
const baseRepoOptions = options?.baseRepo
|
|
306
|
+
? {
|
|
307
|
+
provider: 'github',
|
|
308
|
+
...snakecaseKeys(options.baseRepo as unknown as Record<string, unknown>),
|
|
309
|
+
}
|
|
310
|
+
: null;
|
|
98
311
|
|
|
99
|
-
|
|
100
|
-
|
|
312
|
+
const createRepoPath = baseRepoOptions
|
|
313
|
+
? {
|
|
314
|
+
path: 'repos',
|
|
315
|
+
body: {
|
|
316
|
+
base_repo: baseRepoOptions,
|
|
317
|
+
},
|
|
318
|
+
}
|
|
319
|
+
: 'repos';
|
|
320
|
+
|
|
321
|
+
// Allow 409 so we can map it to a clearer error message
|
|
322
|
+
const resp = await this.api.post(createRepoPath, jwt, { allowedStatus: [409] });
|
|
323
|
+
if (resp.status === 409) {
|
|
324
|
+
throw new Error('Repository already exists');
|
|
101
325
|
}
|
|
102
326
|
|
|
103
|
-
return
|
|
104
|
-
id: repoId,
|
|
105
|
-
getRemoteURL: async (urlOptions?: GetRemoteURLOptions): Promise<string> => {
|
|
106
|
-
const url = new URL(`https://${this.options.name}.${STORAGE_BASE_URL}/${repoId}.git`);
|
|
107
|
-
url.username = `t`;
|
|
108
|
-
url.password = await this.generateJWT(repoId, urlOptions);
|
|
109
|
-
return url.toString();
|
|
110
|
-
},
|
|
111
|
-
};
|
|
327
|
+
return new RepoImpl(repoId, this.options, this.generateJWT.bind(this));
|
|
112
328
|
}
|
|
113
329
|
|
|
114
330
|
/**
|
|
@@ -117,15 +333,19 @@ export class GitStorage {
|
|
|
117
333
|
* @returns The found repository
|
|
118
334
|
*/
|
|
119
335
|
async findOne(options: FindOneOptions): Promise<Repo | null> {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
336
|
+
const jwt = await this.generateJWT(options.id, {
|
|
337
|
+
permissions: ['git:read'],
|
|
338
|
+
ttl: 1 * 60 * 60,
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// Allow 404 to indicate "not found" without throwing
|
|
342
|
+
const resp = await this.api.get('repo', jwt, { allowedStatus: [404] });
|
|
343
|
+
if (resp.status === 404) {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
// On 200, we could validate response, but RepoImpl only needs the repo URL/id
|
|
347
|
+
// const body = await resp.json(); // not required for now
|
|
348
|
+
return new RepoImpl(options.id, this.options, this.generateJWT.bind(this));
|
|
129
349
|
}
|
|
130
350
|
|
|
131
351
|
/**
|
package/src/types.ts
CHANGED
|
@@ -2,11 +2,20 @@
|
|
|
2
2
|
* Type definitions for Pierre Git Storage SDK
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export interface
|
|
5
|
+
export interface OverrideableGitStorageOptions {
|
|
6
|
+
apiBaseUrl?: string;
|
|
7
|
+
storageBaseUrl?: string;
|
|
8
|
+
apiVersion?: ValidAPIVersion;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface GitStorageOptions extends OverrideableGitStorageOptions {
|
|
6
12
|
key: string;
|
|
7
13
|
name: string;
|
|
14
|
+
defaultTTL?: number;
|
|
8
15
|
}
|
|
9
16
|
|
|
17
|
+
export type ValidAPIVersion = 1;
|
|
18
|
+
|
|
10
19
|
export interface GetRemoteURLOptions {
|
|
11
20
|
permissions?: ('git:write' | 'git:read' | 'repo:write')[];
|
|
12
21
|
ttl?: number;
|
|
@@ -15,17 +24,243 @@ export interface GetRemoteURLOptions {
|
|
|
15
24
|
export interface Repo {
|
|
16
25
|
id: string;
|
|
17
26
|
getRemoteURL(options?: GetRemoteURLOptions): Promise<string>;
|
|
27
|
+
getFile(options: GetFileOptions): Promise<GetFileResponse>;
|
|
28
|
+
listFiles(options?: ListFilesOptions): Promise<ListFilesResponse>;
|
|
29
|
+
listBranches(options?: ListBranchesOptions): Promise<ListBranchesResponse>;
|
|
30
|
+
listCommits(options?: ListCommitsOptions): Promise<ListCommitsResponse>;
|
|
31
|
+
getBranchDiff(options: GetBranchDiffOptions): Promise<GetBranchDiffResponse>;
|
|
32
|
+
getCommitDiff(options: GetCommitDiffOptions): Promise<GetCommitDiffResponse>;
|
|
33
|
+
getCommit(options: GetCommitOptions): Promise<GetCommitResponse>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type ValidMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
37
|
+
type SimplePath = string;
|
|
38
|
+
type ComplexPath = {
|
|
39
|
+
path: string;
|
|
40
|
+
params?: Record<string, string>;
|
|
41
|
+
body?: Record<string, any>;
|
|
42
|
+
};
|
|
43
|
+
export type ValidPath = SimplePath | ComplexPath;
|
|
44
|
+
|
|
45
|
+
interface GitStorageInvocationOptions {
|
|
46
|
+
ttl?: number;
|
|
18
47
|
}
|
|
19
48
|
|
|
20
49
|
export interface FindOneOptions {
|
|
21
50
|
id: string;
|
|
22
51
|
}
|
|
23
52
|
|
|
24
|
-
export
|
|
53
|
+
export type SupportedRepoProvider = 'github';
|
|
54
|
+
|
|
55
|
+
export interface BaseRepo {
|
|
56
|
+
/**
|
|
57
|
+
* @default github
|
|
58
|
+
*/
|
|
59
|
+
provider?: SupportedRepoProvider;
|
|
60
|
+
owner: string;
|
|
61
|
+
name: string;
|
|
62
|
+
defaultBranch?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface CreateRepoOptions extends GitStorageInvocationOptions {
|
|
25
66
|
id?: string;
|
|
67
|
+
baseRepo?: BaseRepo;
|
|
26
68
|
}
|
|
27
69
|
|
|
28
70
|
export interface CreateRepoResponse {
|
|
29
71
|
repo_id: string;
|
|
30
72
|
url: string;
|
|
31
73
|
}
|
|
74
|
+
|
|
75
|
+
// Get File API types
|
|
76
|
+
export interface GetFileOptions extends GitStorageInvocationOptions {
|
|
77
|
+
path: string;
|
|
78
|
+
ref?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface GetFileResponse {
|
|
82
|
+
path: string;
|
|
83
|
+
ref: string;
|
|
84
|
+
content: string;
|
|
85
|
+
size: number;
|
|
86
|
+
is_binary: boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface RepullOptions extends GitStorageInvocationOptions {
|
|
90
|
+
ref?: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// List Files API types
|
|
94
|
+
export interface ListFilesOptions extends GitStorageInvocationOptions {
|
|
95
|
+
ref?: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface ListFilesResponse {
|
|
99
|
+
paths: string[];
|
|
100
|
+
ref: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// List Branches API types
|
|
104
|
+
export interface ListBranchesOptions extends GitStorageInvocationOptions {
|
|
105
|
+
cursor?: string;
|
|
106
|
+
limit?: number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface BranchInfo {
|
|
110
|
+
cursor: string;
|
|
111
|
+
name: string;
|
|
112
|
+
head_sha: string;
|
|
113
|
+
created_at: string;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface ListBranchesResponse {
|
|
117
|
+
branches: BranchInfo[];
|
|
118
|
+
next_cursor?: string;
|
|
119
|
+
has_more: boolean;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// List Commits API types
|
|
123
|
+
export interface ListCommitsOptions extends GitStorageInvocationOptions {
|
|
124
|
+
branch?: string;
|
|
125
|
+
cursor?: string;
|
|
126
|
+
limit?: number;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface CommitInfo {
|
|
130
|
+
sha: string;
|
|
131
|
+
message: string;
|
|
132
|
+
author_name: string;
|
|
133
|
+
author_email: string;
|
|
134
|
+
committer_name: string;
|
|
135
|
+
committer_email: string;
|
|
136
|
+
date: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface ListCommitsResponse {
|
|
140
|
+
commits: CommitInfo[];
|
|
141
|
+
next_cursor?: string;
|
|
142
|
+
has_more: boolean;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Branch Diff API types
|
|
146
|
+
export interface GetBranchDiffOptions extends GitStorageInvocationOptions {
|
|
147
|
+
branch: string;
|
|
148
|
+
base?: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface GetBranchDiffResponse {
|
|
152
|
+
branch: string;
|
|
153
|
+
base: string;
|
|
154
|
+
stats: DiffStats;
|
|
155
|
+
files: FileDiff[];
|
|
156
|
+
filtered_files: FilteredFile[];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Commit Diff API types
|
|
160
|
+
export interface GetCommitDiffOptions extends GitStorageInvocationOptions {
|
|
161
|
+
sha: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface GetCommitDiffResponse {
|
|
165
|
+
sha: string;
|
|
166
|
+
stats: DiffStats;
|
|
167
|
+
files: FileDiff[];
|
|
168
|
+
filtered_files: FilteredFile[];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Shared diff types
|
|
172
|
+
export interface DiffStats {
|
|
173
|
+
files: number;
|
|
174
|
+
additions: number;
|
|
175
|
+
deletions: number;
|
|
176
|
+
changes: number;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface FileDiff {
|
|
180
|
+
path: string;
|
|
181
|
+
state: string;
|
|
182
|
+
old_path?: string;
|
|
183
|
+
bytes: number;
|
|
184
|
+
is_eof: boolean;
|
|
185
|
+
diff: string;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface FilteredFile {
|
|
189
|
+
path: string;
|
|
190
|
+
state: string;
|
|
191
|
+
old_path?: string;
|
|
192
|
+
bytes: number;
|
|
193
|
+
is_eof: boolean;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Get Commit API types
|
|
197
|
+
export interface GetCommitOptions extends GitStorageInvocationOptions {
|
|
198
|
+
sha: string;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export interface GetCommitResponse {
|
|
202
|
+
sha: string;
|
|
203
|
+
message: string;
|
|
204
|
+
author: {
|
|
205
|
+
name: string;
|
|
206
|
+
email: string;
|
|
207
|
+
};
|
|
208
|
+
committer: {
|
|
209
|
+
name: string;
|
|
210
|
+
email: string;
|
|
211
|
+
};
|
|
212
|
+
date: string;
|
|
213
|
+
stats: {
|
|
214
|
+
additions: number;
|
|
215
|
+
deletions: number;
|
|
216
|
+
total: number;
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Webhook types
|
|
221
|
+
export interface WebhookValidationOptions {
|
|
222
|
+
/**
|
|
223
|
+
* Maximum age of webhook in seconds (default: 300 seconds / 5 minutes)
|
|
224
|
+
* Set to 0 to disable timestamp validation
|
|
225
|
+
*/
|
|
226
|
+
maxAgeSeconds?: number;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export interface WebhookValidationResult {
|
|
230
|
+
/**
|
|
231
|
+
* Whether the webhook signature and timestamp are valid
|
|
232
|
+
*/
|
|
233
|
+
valid: boolean;
|
|
234
|
+
/**
|
|
235
|
+
* Error message if validation failed
|
|
236
|
+
*/
|
|
237
|
+
error?: string;
|
|
238
|
+
/**
|
|
239
|
+
* The parsed webhook event type (e.g., "push")
|
|
240
|
+
*/
|
|
241
|
+
eventType?: string;
|
|
242
|
+
/**
|
|
243
|
+
* The timestamp from the signature (Unix seconds)
|
|
244
|
+
*/
|
|
245
|
+
timestamp?: number;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Webhook event payloads
|
|
249
|
+
export interface WebhookPushEvent {
|
|
250
|
+
repository: {
|
|
251
|
+
id: string;
|
|
252
|
+
url: string;
|
|
253
|
+
};
|
|
254
|
+
ref: string;
|
|
255
|
+
before: string;
|
|
256
|
+
after: string;
|
|
257
|
+
customer_id: string;
|
|
258
|
+
pushed_at: string; // RFC3339 timestamp
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export type WebhookEventPayload = WebhookPushEvent;
|
|
262
|
+
|
|
263
|
+
export interface ParsedWebhookSignature {
|
|
264
|
+
timestamp: string;
|
|
265
|
+
signature: string;
|
|
266
|
+
}
|