@sourceaxis/provider-github 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/README.md +68 -0
- package/dist/cjs/constants.js +7 -0
- package/dist/cjs/constants.js.map +1 -0
- package/dist/cjs/github-models.js +3 -0
- package/dist/cjs/github-models.js.map +1 -0
- package/dist/cjs/index.js +1127 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/mappers.js +398 -0
- package/dist/cjs/mappers.js.map +1 -0
- package/dist/cjs/octokit-adapter.js +55 -0
- package/dist/cjs/octokit-adapter.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/constants.d.ts +4 -0
- package/dist/esm/constants.d.ts.map +1 -0
- package/dist/esm/constants.js +3 -0
- package/dist/esm/github-models.d.ts +181 -0
- package/dist/esm/github-models.d.ts.map +1 -0
- package/dist/esm/github-models.js +1 -0
- package/dist/esm/index.d.ts +84 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +1112 -0
- package/dist/esm/mappers.d.ts +18 -0
- package/dist/esm/mappers.d.ts.map +1 -0
- package/dist/esm/mappers.js +380 -0
- package/dist/esm/octokit-adapter.d.ts +15 -0
- package/dist/esm/octokit-adapter.d.ts.map +1 -0
- package/dist/esm/octokit-adapter.js +51 -0
- package/package.json +61 -0
|
@@ -0,0 +1,1127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GitHubProviderSession = exports.GitHubProvider = exports.GitHubProviderCapabilities = exports.GitHubProviderVersion = exports.GitHubProviderName = exports.GitHubProviderId = void 0;
|
|
4
|
+
exports.createGitHubProvider = createGitHubProvider;
|
|
5
|
+
exports.githubProvider = githubProvider;
|
|
6
|
+
exports.createGitHubProviderConfig = createGitHubProviderConfig;
|
|
7
|
+
exports.githubTokenAuth = githubTokenAuth;
|
|
8
|
+
exports.createGitHubClient = createGitHubClient;
|
|
9
|
+
exports.mapGitHubError = mapGitHubError;
|
|
10
|
+
const auth_1 = require("@sourceaxis/auth");
|
|
11
|
+
const core_1 = require("@sourceaxis/core");
|
|
12
|
+
const errors_1 = require("@sourceaxis/errors");
|
|
13
|
+
const shared_1 = require("@sourceaxis/shared");
|
|
14
|
+
const transport_1 = require("@sourceaxis/transport");
|
|
15
|
+
const mappers_js_1 = require("./mappers.js");
|
|
16
|
+
var constants_js_1 = require("./constants.js");
|
|
17
|
+
Object.defineProperty(exports, "GitHubProviderId", { enumerable: true, get: function () { return constants_js_1.GitHubProviderId; } });
|
|
18
|
+
Object.defineProperty(exports, "GitHubProviderName", { enumerable: true, get: function () { return constants_js_1.GitHubProviderName; } });
|
|
19
|
+
Object.defineProperty(exports, "GitHubProviderVersion", { enumerable: true, get: function () { return constants_js_1.GitHubProviderVersion; } });
|
|
20
|
+
const constants_js_2 = require("./constants.js");
|
|
21
|
+
const foundationCapabilityDescriptors = (0, shared_1.deepFreeze)([
|
|
22
|
+
{
|
|
23
|
+
name: "files",
|
|
24
|
+
operations: [
|
|
25
|
+
"readText",
|
|
26
|
+
"readJson",
|
|
27
|
+
"readBinary",
|
|
28
|
+
"download",
|
|
29
|
+
"exists",
|
|
30
|
+
"metadata",
|
|
31
|
+
"getMetadata"
|
|
32
|
+
],
|
|
33
|
+
status: "supported"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "tree",
|
|
37
|
+
operations: ["list", "get", "tree"],
|
|
38
|
+
status: "supported"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "history",
|
|
42
|
+
operations: ["list", "get", "file"],
|
|
43
|
+
status: "supported"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "search",
|
|
47
|
+
operations: ["text", "query"],
|
|
48
|
+
status: "supported"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "branches",
|
|
52
|
+
operations: ["list", "get"],
|
|
53
|
+
status: "supported"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "tags",
|
|
57
|
+
operations: ["list", "get"],
|
|
58
|
+
status: "supported"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "issues",
|
|
62
|
+
operations: ["list", "get"],
|
|
63
|
+
status: "supported"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "pullRequests",
|
|
67
|
+
operations: ["list", "get"],
|
|
68
|
+
status: "supported"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: "releases",
|
|
72
|
+
operations: ["list", "get"],
|
|
73
|
+
status: "supported"
|
|
74
|
+
}
|
|
75
|
+
]);
|
|
76
|
+
exports.GitHubProviderCapabilities = (0, shared_1.deepFreeze)({
|
|
77
|
+
branches: foundationCapabilityDescriptors[4],
|
|
78
|
+
files: foundationCapabilityDescriptors[0],
|
|
79
|
+
history: foundationCapabilityDescriptors[2],
|
|
80
|
+
issues: foundationCapabilityDescriptors[6],
|
|
81
|
+
pullRequests: foundationCapabilityDescriptors[7],
|
|
82
|
+
releases: foundationCapabilityDescriptors[8],
|
|
83
|
+
search: foundationCapabilityDescriptors[3],
|
|
84
|
+
tags: foundationCapabilityDescriptors[5],
|
|
85
|
+
tree: foundationCapabilityDescriptors[1]
|
|
86
|
+
});
|
|
87
|
+
/**
|
|
88
|
+
* Creates the GitHub provider implementation for explicit Core registration.
|
|
89
|
+
*/
|
|
90
|
+
function createGitHubProvider(config = {}) {
|
|
91
|
+
return new GitHubProvider(config);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Creates the GitHub provider with a short, discoverable name for application setup.
|
|
95
|
+
*/
|
|
96
|
+
function githubProvider(config = {}) {
|
|
97
|
+
return createGitHubProvider(config);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Creates a SourceAxis client config fragment containing the GitHub provider.
|
|
101
|
+
*/
|
|
102
|
+
function createGitHubProviderConfig(config = {}) {
|
|
103
|
+
return {
|
|
104
|
+
providers: [createGitHubProvider(config)]
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Creates a GitHub-scoped token authentication strategy for SourceAxis clients.
|
|
109
|
+
*/
|
|
110
|
+
function githubTokenAuth(token, options = {}) {
|
|
111
|
+
return Object.freeze({
|
|
112
|
+
type: "token",
|
|
113
|
+
async authenticate(request) {
|
|
114
|
+
if (request?.provider !== undefined && request.provider !== constants_js_2.GitHubProviderId) {
|
|
115
|
+
return (0, auth_1.createAuthContext)((0, auth_1.anonymousAuth)({ provider: request.provider }));
|
|
116
|
+
}
|
|
117
|
+
return (0, auth_1.createAuthContext)((0, auth_1.tokenAuth)({ ...options, provider: constants_js_2.GitHubProviderId, token }));
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Creates a SourceAxis client with the GitHub provider registered.
|
|
123
|
+
*/
|
|
124
|
+
function createGitHubClient(config = {}) {
|
|
125
|
+
const { authentication, token, ...providerConfig } = config;
|
|
126
|
+
const resolvedProviderConfig = {
|
|
127
|
+
...providerConfig,
|
|
128
|
+
transport: providerConfig.transport ?? createGitHubHttpTransport()
|
|
129
|
+
};
|
|
130
|
+
return (0, core_1.createSourceAxisClient)({
|
|
131
|
+
...createGitHubProviderConfig(resolvedProviderConfig),
|
|
132
|
+
authentication: authentication ?? (token === undefined ? undefined : githubTokenAuth(token))
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
class GitHubProvider {
|
|
136
|
+
#config;
|
|
137
|
+
#hosts;
|
|
138
|
+
info;
|
|
139
|
+
constructor(config = {}) {
|
|
140
|
+
this.#hosts = (0, shared_1.deepFreeze)(normalizeHosts(config.hosts ?? ["github.com"]));
|
|
141
|
+
this.#config = Object.freeze({
|
|
142
|
+
...config,
|
|
143
|
+
hosts: this.#hosts
|
|
144
|
+
});
|
|
145
|
+
this.info = (0, shared_1.deepFreeze)({
|
|
146
|
+
capabilities: exports.GitHubProviderCapabilities,
|
|
147
|
+
id: constants_js_2.GitHubProviderId,
|
|
148
|
+
metadata: config.metadata,
|
|
149
|
+
name: constants_js_2.GitHubProviderName,
|
|
150
|
+
priority: config.priority,
|
|
151
|
+
version: constants_js_2.GitHubProviderVersion
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
async supports(locator) {
|
|
155
|
+
const location = parseGitHubRepositoryUrl(locator.url, this.#hosts);
|
|
156
|
+
if (location === undefined) {
|
|
157
|
+
return (0, shared_1.deepFreeze)({ confidence: "none", provider: constants_js_2.GitHubProviderId });
|
|
158
|
+
}
|
|
159
|
+
return (0, shared_1.deepFreeze)({
|
|
160
|
+
confidence: "exact",
|
|
161
|
+
metadata: { provider: constants_js_2.GitHubProviderId, extra: { host: location.host } },
|
|
162
|
+
provider: constants_js_2.GitHubProviderId,
|
|
163
|
+
repository: toRepositoryIdentity(location)
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
async createSession(request) {
|
|
167
|
+
return runGitHubProviderOperation("provider.session.create", async () => {
|
|
168
|
+
const location = resolveRepositoryLocation(request.repository, this.#hosts);
|
|
169
|
+
const dependencies = resolveDependencies(this.#config, request.context);
|
|
170
|
+
const context = createGitHubSessionContext(request.context, dependencies);
|
|
171
|
+
const requestContext = {
|
|
172
|
+
transport: dependencies.transport
|
|
173
|
+
};
|
|
174
|
+
if (context.authenticationContext !== undefined) {
|
|
175
|
+
requestContext.authentication = context.authenticationContext;
|
|
176
|
+
}
|
|
177
|
+
if (context.metadata !== undefined) {
|
|
178
|
+
requestContext.metadata = context.metadata;
|
|
179
|
+
}
|
|
180
|
+
const requestClient = createGitHubRequestClient(requestContext);
|
|
181
|
+
const fallback = toRepositoryInfo(location);
|
|
182
|
+
const repository = await readRepositoryInfo(requestClient, location, fallback);
|
|
183
|
+
return new GitHubProviderSession({
|
|
184
|
+
context,
|
|
185
|
+
requestClient,
|
|
186
|
+
repository
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
exports.GitHubProvider = GitHubProvider;
|
|
192
|
+
class GitHubProviderSession {
|
|
193
|
+
#context;
|
|
194
|
+
#diagnosticSequence = 0;
|
|
195
|
+
#requestClient;
|
|
196
|
+
#state = "active";
|
|
197
|
+
capabilities;
|
|
198
|
+
provider = (0, shared_1.deepFreeze)({
|
|
199
|
+
capabilities: exports.GitHubProviderCapabilities,
|
|
200
|
+
id: constants_js_2.GitHubProviderId,
|
|
201
|
+
name: constants_js_2.GitHubProviderName,
|
|
202
|
+
version: constants_js_2.GitHubProviderVersion
|
|
203
|
+
});
|
|
204
|
+
repository;
|
|
205
|
+
constructor(input) {
|
|
206
|
+
this.#context = Object.freeze(input.context);
|
|
207
|
+
this.#requestClient = input.requestClient;
|
|
208
|
+
this.repository = (0, shared_1.deepFreeze)(input.repository);
|
|
209
|
+
this.capabilities = createFoundationCapabilities(this.repository.identity, this.#requestClient, () => this.ensureActive());
|
|
210
|
+
}
|
|
211
|
+
get state() {
|
|
212
|
+
return this.#state;
|
|
213
|
+
}
|
|
214
|
+
get context() {
|
|
215
|
+
return this.#context;
|
|
216
|
+
}
|
|
217
|
+
async getCapabilities() {
|
|
218
|
+
this.ensureActive();
|
|
219
|
+
return foundationCapabilityDescriptors;
|
|
220
|
+
}
|
|
221
|
+
async dispose() {
|
|
222
|
+
if (this.#state === "disposed") {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
this.#state = "disposed";
|
|
226
|
+
await this.publishDiagnostic("provider.session.dispose");
|
|
227
|
+
}
|
|
228
|
+
ensureActive() {
|
|
229
|
+
if (this.#state === "disposed") {
|
|
230
|
+
throw new errors_1.ProviderError("GitHub provider session has been disposed", {
|
|
231
|
+
diagnostics: {
|
|
232
|
+
operation: { operation: "provider.session.lifecycle" },
|
|
233
|
+
provider: { provider: constants_js_2.GitHubProviderId },
|
|
234
|
+
repository: formatRepositoryDiagnostics(this.repository.identity)
|
|
235
|
+
},
|
|
236
|
+
retryability: "Never"
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async publishDiagnostic(name) {
|
|
241
|
+
try {
|
|
242
|
+
await this.#context.diagnostics?.publish((0, shared_1.deepFreeze)({
|
|
243
|
+
context: {
|
|
244
|
+
provider: constants_js_2.GitHubProviderId,
|
|
245
|
+
repository: `${this.repository.identity.owner}/${this.repository.identity.name}`
|
|
246
|
+
},
|
|
247
|
+
id: `github-${name}-${++this.#diagnosticSequence}`,
|
|
248
|
+
kind: "provider",
|
|
249
|
+
name,
|
|
250
|
+
schemaVersion: "1.0",
|
|
251
|
+
timestamp: new Date().toISOString()
|
|
252
|
+
}));
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// Diagnostics are observational and must not influence provider lifecycle.
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
exports.GitHubProviderSession = GitHubProviderSession;
|
|
260
|
+
function createGitHubRequestClient(context) {
|
|
261
|
+
return (0, shared_1.deepFreeze)({
|
|
262
|
+
async request(request) {
|
|
263
|
+
const transportRequest = toTransportRequest(request, context);
|
|
264
|
+
try {
|
|
265
|
+
const transportContext = {};
|
|
266
|
+
if (context.metadata !== undefined) {
|
|
267
|
+
transportContext.metadata = context.metadata;
|
|
268
|
+
}
|
|
269
|
+
const response = await context.transport.execute(transportRequest, transportContext);
|
|
270
|
+
return fromTransportResponse(response);
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
throw mapGitHubError(error, "github.transport.request");
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
async function readRepositoryInfo(requestClient, location, fallback) {
|
|
279
|
+
const response = await requestGitHubResponse(requestClient, "github.repository.get", `/repos/${encodePathPart(location.owner)}/${encodePathPart(location.name)}`);
|
|
280
|
+
if (response.data === undefined || response.status === 204) {
|
|
281
|
+
return fallback;
|
|
282
|
+
}
|
|
283
|
+
return (0, mappers_js_1.mapGitHubRepository)(response.data, fallback.identity);
|
|
284
|
+
}
|
|
285
|
+
async function readContent(requestClient, repository, path, operation) {
|
|
286
|
+
const response = await requestGitHub(requestClient, operation, `/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/contents/${encodeRepositoryPath(path)}`);
|
|
287
|
+
if (Array.isArray(response)) {
|
|
288
|
+
throw new errors_1.ValidationError("GitHub content response is a directory, not a file", {
|
|
289
|
+
diagnostics: {
|
|
290
|
+
operation: { operation },
|
|
291
|
+
provider: { provider: constants_js_2.GitHubProviderId },
|
|
292
|
+
repository: formatRepositoryDiagnostics(repository)
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
return response;
|
|
297
|
+
}
|
|
298
|
+
async function requestGitHub(requestClient, operation, url) {
|
|
299
|
+
const response = await requestGitHubResponse(requestClient, operation, url);
|
|
300
|
+
if (response.data === undefined) {
|
|
301
|
+
throw new errors_1.ProviderError("GitHub response did not include a response body", {
|
|
302
|
+
diagnostics: {
|
|
303
|
+
operation: { operation },
|
|
304
|
+
provider: { provider: constants_js_2.GitHubProviderId, status: response.status }
|
|
305
|
+
},
|
|
306
|
+
retryability: "Maybe"
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
return response.data;
|
|
310
|
+
}
|
|
311
|
+
async function requestGitHubResponse(requestClient, operation, url) {
|
|
312
|
+
try {
|
|
313
|
+
const response = await requestClient.request({ method: "GET", url });
|
|
314
|
+
if (response.status === 404) {
|
|
315
|
+
throw new errors_1.NotFoundError("GitHub resource was not found", {
|
|
316
|
+
diagnostics: {
|
|
317
|
+
operation: { operation },
|
|
318
|
+
provider: { provider: constants_js_2.GitHubProviderId, status: response.status }
|
|
319
|
+
},
|
|
320
|
+
retryability: "Never"
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
if (response.status < 200 || response.status >= 300) {
|
|
324
|
+
throw createGitHubStatusError(response.status, operation, response.headers);
|
|
325
|
+
}
|
|
326
|
+
return response;
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
throw mapGitHubError(error, operation);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function mapGitHubError(error, operation = "github.provider") {
|
|
333
|
+
if (error instanceof errors_1.SourceAxisError) {
|
|
334
|
+
return error;
|
|
335
|
+
}
|
|
336
|
+
const status = getErrorStatus(error);
|
|
337
|
+
const providerDiagnostics = {
|
|
338
|
+
provider: constants_js_2.GitHubProviderId
|
|
339
|
+
};
|
|
340
|
+
const providerCode = getErrorCode(error);
|
|
341
|
+
if (providerCode !== undefined) {
|
|
342
|
+
providerDiagnostics.providerCode = providerCode;
|
|
343
|
+
}
|
|
344
|
+
const requestId = getErrorRequestId(error);
|
|
345
|
+
if (requestId !== undefined) {
|
|
346
|
+
providerDiagnostics.requestId = requestId;
|
|
347
|
+
}
|
|
348
|
+
if (status !== undefined) {
|
|
349
|
+
providerDiagnostics.status = status;
|
|
350
|
+
}
|
|
351
|
+
const diagnostics = {
|
|
352
|
+
operation: { operation },
|
|
353
|
+
provider: providerDiagnostics
|
|
354
|
+
};
|
|
355
|
+
if (status === 401) {
|
|
356
|
+
return new errors_1.AuthenticationError("GitHub authentication failed", {
|
|
357
|
+
cause: error,
|
|
358
|
+
diagnostics,
|
|
359
|
+
retryability: "Never"
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
if (status === 403 && isRateLimitError(error)) {
|
|
363
|
+
return new errors_1.RateLimitError("GitHub rate limit exceeded", {
|
|
364
|
+
cause: error,
|
|
365
|
+
diagnostics,
|
|
366
|
+
retryability: "Always"
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
if (status === 403) {
|
|
370
|
+
return new errors_1.AuthorizationError("GitHub authorization failed", {
|
|
371
|
+
cause: error,
|
|
372
|
+
diagnostics,
|
|
373
|
+
retryability: "Never"
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
if (status === 404) {
|
|
377
|
+
return new errors_1.NotFoundError("GitHub resource was not found", {
|
|
378
|
+
cause: error,
|
|
379
|
+
diagnostics,
|
|
380
|
+
retryability: "Never"
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
if (status === 409) {
|
|
384
|
+
return new errors_1.ConflictError("GitHub reported a resource conflict", {
|
|
385
|
+
cause: error,
|
|
386
|
+
diagnostics,
|
|
387
|
+
retryability: "Maybe"
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
if (status !== undefined && status >= 500) {
|
|
391
|
+
return new errors_1.ProviderError("GitHub provider request failed", {
|
|
392
|
+
cause: error,
|
|
393
|
+
diagnostics,
|
|
394
|
+
retryability: "Maybe"
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
if (status !== undefined) {
|
|
398
|
+
return new errors_1.ProviderError("GitHub provider rejected the request", {
|
|
399
|
+
cause: error,
|
|
400
|
+
diagnostics,
|
|
401
|
+
retryability: retryabilityForStatus(status)
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
return new errors_1.ProviderError("GitHub provider operation failed", {
|
|
405
|
+
cause: error,
|
|
406
|
+
diagnostics,
|
|
407
|
+
retryability: "Maybe"
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
function createGitHubSessionContext(context, dependencies) {
|
|
411
|
+
return Object.freeze({
|
|
412
|
+
...context,
|
|
413
|
+
cache: dependencies.cache,
|
|
414
|
+
diagnostics: dependencies.diagnostics,
|
|
415
|
+
provider: {
|
|
416
|
+
...context.provider,
|
|
417
|
+
capabilities: exports.GitHubProviderCapabilities
|
|
418
|
+
},
|
|
419
|
+
transport: dependencies.transport
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
function resolveDependencies(config, context) {
|
|
423
|
+
const dependencies = {
|
|
424
|
+
transport: config.transport ?? context.transport ?? createGitHubHttpTransport()
|
|
425
|
+
};
|
|
426
|
+
const cache = config.cache ?? context.cache;
|
|
427
|
+
const diagnostics = config.diagnostics ?? context.diagnostics;
|
|
428
|
+
if (cache !== undefined) {
|
|
429
|
+
dependencies.cache = cache;
|
|
430
|
+
}
|
|
431
|
+
if (diagnostics !== undefined) {
|
|
432
|
+
dependencies.diagnostics = diagnostics;
|
|
433
|
+
}
|
|
434
|
+
return dependencies;
|
|
435
|
+
}
|
|
436
|
+
function createFoundationCapabilities(repository, requestClient, ensureActive) {
|
|
437
|
+
const runtime = createGitHubRuntimeCapabilities(repository, requestClient, ensureActive);
|
|
438
|
+
return (0, shared_1.deepFreeze)({
|
|
439
|
+
branches: runtime.branches,
|
|
440
|
+
files: runtime.files,
|
|
441
|
+
history: runtime.history,
|
|
442
|
+
issues: runtime.issues,
|
|
443
|
+
pullRequests: runtime.pullRequests,
|
|
444
|
+
releases: runtime.releases,
|
|
445
|
+
search: runtime.search,
|
|
446
|
+
tags: runtime.tags,
|
|
447
|
+
tree: runtime.tree
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
function createGitHubRuntimeCapabilities(repository, requestClient, ensureActive) {
|
|
451
|
+
return (0, shared_1.deepFreeze)({
|
|
452
|
+
branches: createGitHubBranchesCapability(repository, requestClient, ensureActive),
|
|
453
|
+
files: createGitHubFilesCapability(repository, requestClient, ensureActive),
|
|
454
|
+
history: createGitHubHistoryCapability(repository, requestClient, ensureActive),
|
|
455
|
+
issues: createGitHubIssuesCapability(repository, requestClient, ensureActive),
|
|
456
|
+
pullRequests: createGitHubPullRequestsCapability(repository, requestClient, ensureActive),
|
|
457
|
+
releases: createGitHubReleasesCapability(repository, requestClient, ensureActive),
|
|
458
|
+
search: createGitHubSearchCapability(repository, requestClient, ensureActive),
|
|
459
|
+
tags: createGitHubTagsCapability(repository, requestClient, ensureActive),
|
|
460
|
+
tree: createGitHubTreeCapability(repository, requestClient, ensureActive)
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
function createGitHubBranchesCapability(repository, requestClient, ensureActive) {
|
|
464
|
+
return (0, shared_1.deepFreeze)({
|
|
465
|
+
async get(name) {
|
|
466
|
+
ensureActive();
|
|
467
|
+
validateNonEmpty(name, "Branch name must be a non-empty string");
|
|
468
|
+
const response = await requestGitHub(requestClient, "github.branches.get", `/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/branches/${encodePathPart(name)}`);
|
|
469
|
+
return (0, mappers_js_1.mapGitHubBranch)(response, repository);
|
|
470
|
+
},
|
|
471
|
+
async list(_options) {
|
|
472
|
+
ensureActive();
|
|
473
|
+
const response = await requestGitHub(requestClient, "github.branches.list", `/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/branches`);
|
|
474
|
+
return createPage(response.map((branch) => (0, mappers_js_1.mapGitHubBranch)(branch, repository)));
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
function createGitHubTagsCapability(repository, requestClient, ensureActive) {
|
|
479
|
+
return (0, shared_1.deepFreeze)({
|
|
480
|
+
async get(name) {
|
|
481
|
+
ensureActive();
|
|
482
|
+
validateNonEmpty(name, "Tag name must be a non-empty string");
|
|
483
|
+
const response = await requestGitHub(requestClient, "github.tags.get", `/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/git/ref/tags/${encodePathPart(name)}`);
|
|
484
|
+
return (0, mappers_js_1.mapGitHubTag)(response, repository);
|
|
485
|
+
},
|
|
486
|
+
async list(_options) {
|
|
487
|
+
ensureActive();
|
|
488
|
+
const response = await requestGitHub(requestClient, "github.tags.list", `/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/git/refs/tags`);
|
|
489
|
+
return createPage(response.map((tag) => (0, mappers_js_1.mapGitHubTag)(tag, repository)));
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
function createGitHubHistoryCapability(repository, requestClient, ensureActive) {
|
|
494
|
+
return (0, shared_1.deepFreeze)({
|
|
495
|
+
async file(path, _options) {
|
|
496
|
+
ensureActive();
|
|
497
|
+
validatePath(path);
|
|
498
|
+
const response = await requestGitHub(requestClient, "github.history.file", `/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/commits?path=${encodeURIComponent(path)}`);
|
|
499
|
+
return createPage(response.map(mappers_js_1.mapGitHubCommit));
|
|
500
|
+
},
|
|
501
|
+
async get(sha) {
|
|
502
|
+
ensureActive();
|
|
503
|
+
validateNonEmpty(sha, "Commit sha must be a non-empty string");
|
|
504
|
+
const response = await requestGitHub(requestClient, "github.history.get", `/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/commits/${encodePathPart(sha)}`);
|
|
505
|
+
return (0, mappers_js_1.mapGitHubCommit)(response);
|
|
506
|
+
},
|
|
507
|
+
async list(_options) {
|
|
508
|
+
ensureActive();
|
|
509
|
+
const response = await requestGitHub(requestClient, "github.history.list", `/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/commits`);
|
|
510
|
+
return createPage(response.map(mappers_js_1.mapGitHubCommit));
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
function createGitHubTreeCapability(repository, requestClient, ensureActive) {
|
|
515
|
+
return (0, shared_1.deepFreeze)({
|
|
516
|
+
async get(path) {
|
|
517
|
+
ensureActive();
|
|
518
|
+
validatePath(path);
|
|
519
|
+
const response = await requestGitHub(requestClient, "github.tree.get", `/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/contents/${encodeRepositoryPath(path)}`);
|
|
520
|
+
return (0, mappers_js_1.mapGitHubContentToTreeNode)(response);
|
|
521
|
+
},
|
|
522
|
+
async list(path) {
|
|
523
|
+
ensureActive();
|
|
524
|
+
validateOptionalPath(path);
|
|
525
|
+
const suffix = path === undefined ? "" : `/${encodeRepositoryPath(path)}`;
|
|
526
|
+
const response = await requestGitHub(requestClient, "github.tree.list", `/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/contents${suffix}`);
|
|
527
|
+
const items = Array.isArray(response) ? response : [response];
|
|
528
|
+
return (0, shared_1.deepFreeze)(items.map(mappers_js_1.mapGitHubContentToTreeNode));
|
|
529
|
+
},
|
|
530
|
+
async tree(path) {
|
|
531
|
+
ensureActive();
|
|
532
|
+
validateOptionalPath(path);
|
|
533
|
+
const ref = path ?? "HEAD";
|
|
534
|
+
const response = await requestGitHub(requestClient, "github.tree.tree", `/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/git/trees/${encodePathPart(ref)}`);
|
|
535
|
+
return (0, mappers_js_1.mapGitHubTree)(response, path ?? "");
|
|
536
|
+
},
|
|
537
|
+
walk() {
|
|
538
|
+
ensureActive();
|
|
539
|
+
return rejectUnsupportedIterable("tree", repository);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
function createGitHubFilesCapability(repository, requestClient, ensureActive) {
|
|
544
|
+
return (0, shared_1.deepFreeze)({
|
|
545
|
+
async download(path) {
|
|
546
|
+
ensureActive();
|
|
547
|
+
validatePath(path);
|
|
548
|
+
const response = await readContent(requestClient, repository, path, "github.files.download");
|
|
549
|
+
return (0, mappers_js_1.mapGitHubContentToBlob)(response);
|
|
550
|
+
},
|
|
551
|
+
async exists(path) {
|
|
552
|
+
ensureActive();
|
|
553
|
+
validatePath(path);
|
|
554
|
+
try {
|
|
555
|
+
await readContent(requestClient, repository, path, "github.files.exists");
|
|
556
|
+
return true;
|
|
557
|
+
}
|
|
558
|
+
catch (error) {
|
|
559
|
+
if (error instanceof errors_1.NotFoundError) {
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
throw error;
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
async metadata(path) {
|
|
566
|
+
ensureActive();
|
|
567
|
+
validatePath(path);
|
|
568
|
+
return (0, mappers_js_1.mapGitHubContentToFileInfo)(await readContent(requestClient, repository, path, "github.files.metadata"));
|
|
569
|
+
},
|
|
570
|
+
async getMetadata(path) {
|
|
571
|
+
return this.metadata(path);
|
|
572
|
+
},
|
|
573
|
+
async readBinary(path) {
|
|
574
|
+
ensureActive();
|
|
575
|
+
validatePath(path);
|
|
576
|
+
const blob = (0, mappers_js_1.mapGitHubContentToBlob)(await readContent(requestClient, repository, path, "github.files.readBinary"));
|
|
577
|
+
return decodeBlobContent(blob);
|
|
578
|
+
},
|
|
579
|
+
async readJson(path) {
|
|
580
|
+
ensureActive();
|
|
581
|
+
const text = await this.readText(path);
|
|
582
|
+
return JSON.parse(text);
|
|
583
|
+
},
|
|
584
|
+
async readText(path) {
|
|
585
|
+
ensureActive();
|
|
586
|
+
validatePath(path);
|
|
587
|
+
return new TextDecoder().decode(decodeBlobContent((0, mappers_js_1.mapGitHubContentToBlob)(await readContent(requestClient, repository, path, "github.files.readText"))));
|
|
588
|
+
},
|
|
589
|
+
async stream(path) {
|
|
590
|
+
ensureActive();
|
|
591
|
+
validatePath(path);
|
|
592
|
+
const content = await this.readBinary(path);
|
|
593
|
+
return (async function* streamContent() {
|
|
594
|
+
yield content;
|
|
595
|
+
})();
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
function createGitHubIssuesCapability(repository, requestClient, ensureActive) {
|
|
600
|
+
return (0, shared_1.deepFreeze)({
|
|
601
|
+
async get(number) {
|
|
602
|
+
ensureActive();
|
|
603
|
+
validatePositiveInteger(number, "Issue number must be a positive integer");
|
|
604
|
+
const response = await requestGitHub(requestClient, "github.issues.get", `/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/issues/${number}`);
|
|
605
|
+
return (0, mappers_js_1.mapGitHubIssue)(response);
|
|
606
|
+
},
|
|
607
|
+
async list(options) {
|
|
608
|
+
ensureActive();
|
|
609
|
+
return requestGitHubPage(requestClient, "github.issues.list", withPagination(`/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/issues`, options), mappers_js_1.mapGitHubIssue);
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
function createGitHubPullRequestsCapability(repository, requestClient, ensureActive) {
|
|
614
|
+
return (0, shared_1.deepFreeze)({
|
|
615
|
+
async get(number) {
|
|
616
|
+
ensureActive();
|
|
617
|
+
validatePositiveInteger(number, "Pull request number must be a positive integer");
|
|
618
|
+
const response = await requestGitHub(requestClient, "github.pullRequests.get", `/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/pulls/${number}`);
|
|
619
|
+
return (0, mappers_js_1.mapGitHubPullRequest)(response, repository);
|
|
620
|
+
},
|
|
621
|
+
async list(options) {
|
|
622
|
+
ensureActive();
|
|
623
|
+
return requestGitHubPage(requestClient, "github.pullRequests.list", withPagination(`/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/pulls`, options), (pullRequest) => (0, mappers_js_1.mapGitHubPullRequest)(pullRequest, repository));
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
function createGitHubReleasesCapability(repository, requestClient, ensureActive) {
|
|
628
|
+
return (0, shared_1.deepFreeze)({
|
|
629
|
+
async get(tagName) {
|
|
630
|
+
ensureActive();
|
|
631
|
+
validateNonEmpty(tagName, "Release tag name must be a non-empty string");
|
|
632
|
+
const response = await requestGitHub(requestClient, "github.releases.get", `/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/releases/tags/${encodePathPart(tagName)}`);
|
|
633
|
+
return (0, mappers_js_1.mapGitHubRelease)(response);
|
|
634
|
+
},
|
|
635
|
+
async list(options) {
|
|
636
|
+
ensureActive();
|
|
637
|
+
return requestGitHubPage(requestClient, "github.releases.list", withPagination(`/repos/${encodePathPart(repository.owner)}/${encodePathPart(repository.name)}/releases`, options), mappers_js_1.mapGitHubRelease);
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
function createGitHubSearchCapability(repository, requestClient, ensureActive) {
|
|
642
|
+
return (0, shared_1.deepFreeze)({
|
|
643
|
+
async query(query) {
|
|
644
|
+
ensureActive();
|
|
645
|
+
validateNonEmpty(query.text, "Search query text must be a non-empty string");
|
|
646
|
+
const pathQualifier = query.path === undefined ? "" : ` path:${query.path}`;
|
|
647
|
+
const response = await requestGitHub(requestClient, "github.search.query", withPagination(`/search/code?q=${encodeURIComponent(`${query.text} repo:${repository.owner}/${repository.name}${pathQualifier}`)}`, query));
|
|
648
|
+
return createSearchPage(response);
|
|
649
|
+
},
|
|
650
|
+
async text(query, options) {
|
|
651
|
+
ensureActive();
|
|
652
|
+
validateNonEmpty(query, "Search query text must be a non-empty string");
|
|
653
|
+
return this.query({ text: query, ...(options ?? {}) });
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
async function requestGitHubPage(requestClient, operation, url, map) {
|
|
658
|
+
const response = await requestGitHubResponse(requestClient, operation, url);
|
|
659
|
+
if (response.data === undefined) {
|
|
660
|
+
throw new errors_1.ProviderError("GitHub response did not include a response body", {
|
|
661
|
+
diagnostics: {
|
|
662
|
+
operation: { operation },
|
|
663
|
+
provider: { provider: constants_js_2.GitHubProviderId, status: response.status }
|
|
664
|
+
},
|
|
665
|
+
retryability: "Maybe"
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
return createPage(response.data.map(map), response.headers);
|
|
669
|
+
}
|
|
670
|
+
function createSearchPage(response) {
|
|
671
|
+
return (0, shared_1.deepFreeze)({
|
|
672
|
+
items: (response.items ?? []).map((item) => (0, mappers_js_1.mapGitHubSearchItem)(item)),
|
|
673
|
+
pageInfo: {
|
|
674
|
+
hasNextPage: false,
|
|
675
|
+
hasPreviousPage: false,
|
|
676
|
+
totalCount: response.total_count
|
|
677
|
+
}
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
async function rejectUnsupported(capability, repository) {
|
|
681
|
+
throw new errors_1.CapabilityNotSupportedError("GitHub provider capability operation is not supported", {
|
|
682
|
+
diagnostics: {
|
|
683
|
+
operation: { capability, operation: `github.${capability}` },
|
|
684
|
+
provider: { provider: constants_js_2.GitHubProviderId },
|
|
685
|
+
repository: formatRepositoryDiagnostics(repository)
|
|
686
|
+
},
|
|
687
|
+
retryability: "Never"
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
async function* rejectUnsupportedIterable(capability, repository) {
|
|
691
|
+
await rejectUnsupported(capability, repository);
|
|
692
|
+
}
|
|
693
|
+
function parseGitHubRepositoryUrl(value, hosts) {
|
|
694
|
+
try {
|
|
695
|
+
const url = new URL(value);
|
|
696
|
+
const host = normalizeHost(url.hostname);
|
|
697
|
+
if (!hosts.includes(host)) {
|
|
698
|
+
return undefined;
|
|
699
|
+
}
|
|
700
|
+
const parts = url.pathname
|
|
701
|
+
.replace(/\.git$/u, "")
|
|
702
|
+
.split("/")
|
|
703
|
+
.filter((part) => part.length > 0);
|
|
704
|
+
const offset = host === "api.github.com" && parts[0] === "repos" ? 1 : 0;
|
|
705
|
+
const owner = parts[offset];
|
|
706
|
+
const name = parts[offset + 1];
|
|
707
|
+
if (owner === undefined || name === undefined) {
|
|
708
|
+
return undefined;
|
|
709
|
+
}
|
|
710
|
+
return { host, name, owner, url: `https://${host}/${owner}/${name}` };
|
|
711
|
+
}
|
|
712
|
+
catch {
|
|
713
|
+
return undefined;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
function resolveRepositoryLocation(repository, hosts) {
|
|
717
|
+
if ("url" in repository) {
|
|
718
|
+
const location = parseGitHubRepositoryUrl(repository.url, hosts);
|
|
719
|
+
if (location === undefined) {
|
|
720
|
+
throw new errors_1.ValidationError("Repository locator is not a supported GitHub URL", {
|
|
721
|
+
diagnostics: {
|
|
722
|
+
operation: { operation: "github.repository.resolve" },
|
|
723
|
+
provider: { provider: constants_js_2.GitHubProviderId },
|
|
724
|
+
extra: { url: repository.url }
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
return location;
|
|
729
|
+
}
|
|
730
|
+
validateRepositoryIdentity(repository);
|
|
731
|
+
return {
|
|
732
|
+
host: hosts[0] ?? "github.com",
|
|
733
|
+
name: repository.name,
|
|
734
|
+
owner: repository.owner,
|
|
735
|
+
url: `https://${hosts[0] ?? "github.com"}/${repository.owner}/${repository.name}`
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
function toRepositoryIdentity(location) {
|
|
739
|
+
return (0, shared_1.deepFreeze)({
|
|
740
|
+
name: location.name,
|
|
741
|
+
owner: location.owner,
|
|
742
|
+
provider: constants_js_2.GitHubProviderId
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
function toRepositoryInfo(location) {
|
|
746
|
+
const identity = toRepositoryIdentity(location);
|
|
747
|
+
return (0, shared_1.deepFreeze)({
|
|
748
|
+
fullName: `${identity.owner}/${identity.name}`,
|
|
749
|
+
identity,
|
|
750
|
+
metadata: { provider: constants_js_2.GitHubProviderId, extra: { host: location.host } },
|
|
751
|
+
name: identity.name,
|
|
752
|
+
owner: { username: identity.owner },
|
|
753
|
+
url: location.url,
|
|
754
|
+
visibility: "unknown"
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
function toTransportRequest(request, context) {
|
|
758
|
+
const headers = {
|
|
759
|
+
accept: "application/vnd.github+json",
|
|
760
|
+
...authorizationHeader(context.authentication),
|
|
761
|
+
...request.headers
|
|
762
|
+
};
|
|
763
|
+
return (0, shared_1.deepFreeze)({
|
|
764
|
+
body: request.body,
|
|
765
|
+
headers,
|
|
766
|
+
method: toTransportMethod(request.method),
|
|
767
|
+
metadata: {
|
|
768
|
+
provider: constants_js_2.GitHubProviderId,
|
|
769
|
+
...context.metadata
|
|
770
|
+
},
|
|
771
|
+
signal: request.signal,
|
|
772
|
+
target: request.url,
|
|
773
|
+
timeoutMs: request.timeoutMs
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
function fromTransportResponse(response) {
|
|
777
|
+
const data = getResponseData(response);
|
|
778
|
+
return (0, shared_1.deepFreeze)({
|
|
779
|
+
data,
|
|
780
|
+
headers: response.headers,
|
|
781
|
+
status: response.status
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
function getResponseData(response) {
|
|
785
|
+
const record = response;
|
|
786
|
+
return record.body ?? record.data;
|
|
787
|
+
}
|
|
788
|
+
function toTransportMethod(method) {
|
|
789
|
+
if (method === undefined || method === "GET") {
|
|
790
|
+
return "read";
|
|
791
|
+
}
|
|
792
|
+
if (method === "DELETE") {
|
|
793
|
+
return "delete";
|
|
794
|
+
}
|
|
795
|
+
return "write";
|
|
796
|
+
}
|
|
797
|
+
function authorizationHeader(authentication) {
|
|
798
|
+
const credentials = authentication?.credentials;
|
|
799
|
+
if (credentials === undefined || credentials.kind === "anonymous" || !("token" in credentials)) {
|
|
800
|
+
return {};
|
|
801
|
+
}
|
|
802
|
+
return { authorization: `Bearer ${credentials.token}` };
|
|
803
|
+
}
|
|
804
|
+
function createGitHubHttpTransport() {
|
|
805
|
+
return (0, shared_1.deepFreeze)({
|
|
806
|
+
async execute(request) {
|
|
807
|
+
const response = await executeGitHubHttpRequest(request);
|
|
808
|
+
return (0, transport_1.createTransportResponse)(response);
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
async function executeGitHubHttpRequest(request) {
|
|
813
|
+
const requestUrl = toGitHubApiUrl(request.target);
|
|
814
|
+
const init = {
|
|
815
|
+
method: toHttpMethod(request.method)
|
|
816
|
+
};
|
|
817
|
+
const requestBody = toHttpRequestBody(request.body);
|
|
818
|
+
if (requestBody !== undefined) {
|
|
819
|
+
init.body = requestBody;
|
|
820
|
+
}
|
|
821
|
+
if (request.headers !== undefined) {
|
|
822
|
+
init.headers = request.headers;
|
|
823
|
+
}
|
|
824
|
+
if (request.signal !== undefined) {
|
|
825
|
+
init.signal = request.signal;
|
|
826
|
+
}
|
|
827
|
+
const response = await getFetchImplementation()(requestUrl, init);
|
|
828
|
+
const responseBody = await readHttpResponseBody(response);
|
|
829
|
+
const transportResponse = {
|
|
830
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
831
|
+
status: response.status
|
|
832
|
+
};
|
|
833
|
+
if (responseBody !== undefined) {
|
|
834
|
+
transportResponse.body = responseBody;
|
|
835
|
+
}
|
|
836
|
+
return transportResponse;
|
|
837
|
+
}
|
|
838
|
+
function getFetchImplementation() {
|
|
839
|
+
const fetchImplementation = globalThis["fetch"];
|
|
840
|
+
if (typeof fetchImplementation !== "function") {
|
|
841
|
+
throw new errors_1.TransportError("GitHub provider requires a fetch-compatible runtime", {
|
|
842
|
+
diagnostics: {
|
|
843
|
+
operation: { operation: "github.transport.configure" },
|
|
844
|
+
provider: { provider: constants_js_2.GitHubProviderId }
|
|
845
|
+
},
|
|
846
|
+
retryability: "Never"
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
return fetchImplementation.bind(globalThis);
|
|
850
|
+
}
|
|
851
|
+
function toGitHubApiUrl(target) {
|
|
852
|
+
if (/^https?:\/\//u.test(target)) {
|
|
853
|
+
return target;
|
|
854
|
+
}
|
|
855
|
+
return `https://api.github.com${target.startsWith("/") ? target : `/${target}`}`;
|
|
856
|
+
}
|
|
857
|
+
function toHttpMethod(method) {
|
|
858
|
+
switch (method) {
|
|
859
|
+
case "delete":
|
|
860
|
+
return "DELETE";
|
|
861
|
+
case "write":
|
|
862
|
+
return "POST";
|
|
863
|
+
case "read":
|
|
864
|
+
case "stream":
|
|
865
|
+
return "GET";
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
function toHttpRequestBody(body) {
|
|
869
|
+
if (body === undefined) {
|
|
870
|
+
return undefined;
|
|
871
|
+
}
|
|
872
|
+
if (typeof body === "string" || body instanceof Uint8Array) {
|
|
873
|
+
return body;
|
|
874
|
+
}
|
|
875
|
+
return JSON.stringify(body);
|
|
876
|
+
}
|
|
877
|
+
async function readHttpResponseBody(response) {
|
|
878
|
+
if (response.status === 204 || response.status === 205) {
|
|
879
|
+
return undefined;
|
|
880
|
+
}
|
|
881
|
+
const text = await response.text();
|
|
882
|
+
if (text.length === 0) {
|
|
883
|
+
return undefined;
|
|
884
|
+
}
|
|
885
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
886
|
+
if (contentType.includes("application/json")) {
|
|
887
|
+
return JSON.parse(text);
|
|
888
|
+
}
|
|
889
|
+
return text;
|
|
890
|
+
}
|
|
891
|
+
function createGitHubStatusError(status, operation, headers) {
|
|
892
|
+
if (status === 401) {
|
|
893
|
+
return new errors_1.AuthenticationError("GitHub authentication failed", {
|
|
894
|
+
diagnostics: {
|
|
895
|
+
operation: { operation },
|
|
896
|
+
provider: { provider: constants_js_2.GitHubProviderId, status }
|
|
897
|
+
},
|
|
898
|
+
retryability: "Never"
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
if (status === 403 && isRateLimitError({ headers, status })) {
|
|
902
|
+
return new errors_1.RateLimitError("GitHub rate limit exceeded", {
|
|
903
|
+
diagnostics: {
|
|
904
|
+
operation: { operation },
|
|
905
|
+
provider: { provider: constants_js_2.GitHubProviderId, status }
|
|
906
|
+
},
|
|
907
|
+
retryability: "Always"
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
if (status === 403) {
|
|
911
|
+
return new errors_1.AuthorizationError("GitHub authorization failed", {
|
|
912
|
+
diagnostics: {
|
|
913
|
+
operation: { operation },
|
|
914
|
+
provider: { provider: constants_js_2.GitHubProviderId, status }
|
|
915
|
+
},
|
|
916
|
+
retryability: "Never"
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
if (status === 404) {
|
|
920
|
+
return new errors_1.NotFoundError("GitHub resource was not found", {
|
|
921
|
+
diagnostics: {
|
|
922
|
+
operation: { operation },
|
|
923
|
+
provider: { provider: constants_js_2.GitHubProviderId, status }
|
|
924
|
+
},
|
|
925
|
+
retryability: "Never"
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
if (status === 409) {
|
|
929
|
+
return new errors_1.ConflictError("GitHub reported a resource conflict", {
|
|
930
|
+
diagnostics: {
|
|
931
|
+
operation: { operation },
|
|
932
|
+
provider: { provider: constants_js_2.GitHubProviderId, status }
|
|
933
|
+
},
|
|
934
|
+
retryability: "Maybe"
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
return new errors_1.ProviderError("GitHub provider rejected the request", {
|
|
938
|
+
diagnostics: {
|
|
939
|
+
operation: { operation },
|
|
940
|
+
provider: { provider: constants_js_2.GitHubProviderId, status }
|
|
941
|
+
},
|
|
942
|
+
retryability: retryabilityForStatus(status)
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
function createPage(items, headers) {
|
|
946
|
+
const link = headers?.link ?? headers?.Link;
|
|
947
|
+
return (0, shared_1.deepFreeze)({
|
|
948
|
+
items,
|
|
949
|
+
pageInfo: parsePaginationInfo(link)
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
function parsePaginationInfo(link) {
|
|
953
|
+
if (link === undefined) {
|
|
954
|
+
return {
|
|
955
|
+
hasNextPage: false,
|
|
956
|
+
hasPreviousPage: false
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
const info = {
|
|
960
|
+
hasNextPage: link.includes('rel="next"'),
|
|
961
|
+
hasPreviousPage: link.includes('rel="prev"')
|
|
962
|
+
};
|
|
963
|
+
const next = extractPageCursor(link, "next");
|
|
964
|
+
const previous = extractPageCursor(link, "prev");
|
|
965
|
+
if (next !== undefined) {
|
|
966
|
+
info.endCursor = next;
|
|
967
|
+
}
|
|
968
|
+
if (previous !== undefined) {
|
|
969
|
+
info.startCursor = previous;
|
|
970
|
+
}
|
|
971
|
+
return info;
|
|
972
|
+
}
|
|
973
|
+
function extractPageCursor(link, relation) {
|
|
974
|
+
const match = new RegExp(`<[^>]*[?&]page=([^>&]+)[^>]*>;\\s*rel="${relation}"`, "u").exec(link);
|
|
975
|
+
return match?.[1];
|
|
976
|
+
}
|
|
977
|
+
function withPagination(url, options) {
|
|
978
|
+
const parameters = [];
|
|
979
|
+
if (options?.cursor !== undefined) {
|
|
980
|
+
parameters.push(`page=${encodeURIComponent(options.cursor)}`);
|
|
981
|
+
}
|
|
982
|
+
if (options?.limit !== undefined) {
|
|
983
|
+
parameters.push(`per_page=${encodeURIComponent(String(options.limit))}`);
|
|
984
|
+
}
|
|
985
|
+
if (parameters.length === 0) {
|
|
986
|
+
return url;
|
|
987
|
+
}
|
|
988
|
+
return `${url}${url.includes("?") ? "&" : "?"}${parameters.join("&")}`;
|
|
989
|
+
}
|
|
990
|
+
function decodeBlobContent(blob) {
|
|
991
|
+
if (blob.content instanceof Uint8Array) {
|
|
992
|
+
return blob.content;
|
|
993
|
+
}
|
|
994
|
+
if (blob.content === undefined) {
|
|
995
|
+
return new Uint8Array();
|
|
996
|
+
}
|
|
997
|
+
if (blob.encoding === "base64") {
|
|
998
|
+
return Buffer.from(blob.content.replace(/\s+/gu, ""), "base64");
|
|
999
|
+
}
|
|
1000
|
+
return new TextEncoder().encode(blob.content);
|
|
1001
|
+
}
|
|
1002
|
+
function encodePathPart(value) {
|
|
1003
|
+
return encodeURIComponent(value);
|
|
1004
|
+
}
|
|
1005
|
+
function encodeRepositoryPath(path) {
|
|
1006
|
+
return path.split("/").map(encodePathPart).join("/");
|
|
1007
|
+
}
|
|
1008
|
+
async function runGitHubProviderOperation(operation, execute) {
|
|
1009
|
+
try {
|
|
1010
|
+
return await execute();
|
|
1011
|
+
}
|
|
1012
|
+
catch (error) {
|
|
1013
|
+
throw mapGitHubError(error, operation);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
function getErrorStatus(error) {
|
|
1017
|
+
return getNumericProperty(error, "status") ?? getNumericProperty(error, "statusCode");
|
|
1018
|
+
}
|
|
1019
|
+
function getErrorCode(error) {
|
|
1020
|
+
return getStringProperty(error, "code");
|
|
1021
|
+
}
|
|
1022
|
+
function getErrorRequestId(error) {
|
|
1023
|
+
const headers = getHeaders(error);
|
|
1024
|
+
return headers?.["x-github-request-id"] ?? headers?.["x-request-id"];
|
|
1025
|
+
}
|
|
1026
|
+
function isRateLimitError(error) {
|
|
1027
|
+
const headers = getHeaders(error);
|
|
1028
|
+
return headers?.["x-ratelimit-remaining"] === "0" || getErrorCode(error) === "rate_limit";
|
|
1029
|
+
}
|
|
1030
|
+
function getHeaders(error) {
|
|
1031
|
+
if (error === null || typeof error !== "object" || !("headers" in error)) {
|
|
1032
|
+
return undefined;
|
|
1033
|
+
}
|
|
1034
|
+
const headers = error.headers;
|
|
1035
|
+
if (headers === null || typeof headers !== "object") {
|
|
1036
|
+
return undefined;
|
|
1037
|
+
}
|
|
1038
|
+
const normalized = {};
|
|
1039
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
1040
|
+
if (typeof value === "string") {
|
|
1041
|
+
normalized[key.toLowerCase()] = value;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
return normalized;
|
|
1045
|
+
}
|
|
1046
|
+
function getNumericProperty(error, key) {
|
|
1047
|
+
if (error === null || typeof error !== "object" || !(key in error)) {
|
|
1048
|
+
return undefined;
|
|
1049
|
+
}
|
|
1050
|
+
const value = error[key];
|
|
1051
|
+
return typeof value === "number" ? value : undefined;
|
|
1052
|
+
}
|
|
1053
|
+
function getStringProperty(error, key) {
|
|
1054
|
+
if (error === null || typeof error !== "object" || !(key in error)) {
|
|
1055
|
+
return undefined;
|
|
1056
|
+
}
|
|
1057
|
+
const value = error[key];
|
|
1058
|
+
return typeof value === "string" ? value : undefined;
|
|
1059
|
+
}
|
|
1060
|
+
function retryabilityForStatus(status) {
|
|
1061
|
+
return status === 408 || status === 409 || status === 429 ? "Maybe" : "Never";
|
|
1062
|
+
}
|
|
1063
|
+
function normalizeHosts(hosts) {
|
|
1064
|
+
const normalized = hosts.map(normalizeHost);
|
|
1065
|
+
if (normalized.length === 0) {
|
|
1066
|
+
throw new errors_1.ValidationError("GitHub provider requires at least one host", {
|
|
1067
|
+
diagnostics: {
|
|
1068
|
+
operation: { operation: "github.provider.configure" },
|
|
1069
|
+
provider: { provider: constants_js_2.GitHubProviderId }
|
|
1070
|
+
}
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
return [...new Set(normalized)];
|
|
1074
|
+
}
|
|
1075
|
+
function normalizeHost(host) {
|
|
1076
|
+
validateNonEmpty(host, "GitHub host must be a non-empty string");
|
|
1077
|
+
return host.toLowerCase().replace(/^www\./u, "");
|
|
1078
|
+
}
|
|
1079
|
+
function validateRepositoryIdentity(identity) {
|
|
1080
|
+
validateNonEmpty(identity.provider, "Repository provider must be a non-empty string");
|
|
1081
|
+
validateNonEmpty(identity.owner, "Repository owner must be a non-empty string");
|
|
1082
|
+
validateNonEmpty(identity.name, "Repository name must be a non-empty string");
|
|
1083
|
+
if (identity.provider !== constants_js_2.GitHubProviderId) {
|
|
1084
|
+
throw new errors_1.ValidationError("Repository identity does not belong to the GitHub provider", {
|
|
1085
|
+
diagnostics: {
|
|
1086
|
+
operation: { operation: "github.repository.validate" },
|
|
1087
|
+
provider: { provider: constants_js_2.GitHubProviderId },
|
|
1088
|
+
repository: formatRepositoryDiagnostics(identity)
|
|
1089
|
+
}
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
function validatePath(path) {
|
|
1094
|
+
validateNonEmpty(path, "Repository path must be a non-empty string");
|
|
1095
|
+
}
|
|
1096
|
+
function validateOptionalPath(path) {
|
|
1097
|
+
if (path !== undefined) {
|
|
1098
|
+
validatePath(path);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
function validatePositiveInteger(value, message) {
|
|
1102
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
1103
|
+
throw new errors_1.ValidationError(message, {
|
|
1104
|
+
diagnostics: {
|
|
1105
|
+
operation: { operation: "github.provider.validate" },
|
|
1106
|
+
provider: { provider: constants_js_2.GitHubProviderId }
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
function validateNonEmpty(value, message) {
|
|
1112
|
+
if (value.trim() === "") {
|
|
1113
|
+
throw new errors_1.ValidationError(message, {
|
|
1114
|
+
diagnostics: {
|
|
1115
|
+
operation: { operation: "github.provider.validate" },
|
|
1116
|
+
provider: { provider: constants_js_2.GitHubProviderId }
|
|
1117
|
+
}
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
function formatRepositoryDiagnostics(identity) {
|
|
1122
|
+
return {
|
|
1123
|
+
provider: identity.provider,
|
|
1124
|
+
repository: `${identity.owner}/${identity.name}`
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
//# sourceMappingURL=index.js.map
|