@dependabit/github-client 0.1.1
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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +266 -0
- package/dist/auth/basic.d.ts +46 -0
- package/dist/auth/basic.d.ts.map +1 -0
- package/dist/auth/basic.js +88 -0
- package/dist/auth/basic.js.map +1 -0
- package/dist/auth/oauth.d.ts +48 -0
- package/dist/auth/oauth.d.ts.map +1 -0
- package/dist/auth/oauth.js +139 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/token.d.ts +40 -0
- package/dist/auth/token.d.ts.map +1 -0
- package/dist/auth/token.js +67 -0
- package/dist/auth/token.js.map +1 -0
- package/dist/auth.d.ts +47 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +78 -0
- package/dist/auth.js.map +1 -0
- package/dist/client.d.ts +53 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +74 -0
- package/dist/client.js.map +1 -0
- package/dist/commits.d.ts +57 -0
- package/dist/commits.d.ts.map +1 -0
- package/dist/commits.js +113 -0
- package/dist/commits.js.map +1 -0
- package/dist/feedback.d.ts +69 -0
- package/dist/feedback.d.ts.map +1 -0
- package/dist/feedback.js +111 -0
- package/dist/feedback.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/issues.d.ts +55 -0
- package/dist/issues.d.ts.map +1 -0
- package/dist/issues.js +123 -0
- package/dist/issues.js.map +1 -0
- package/dist/rate-limit.d.ts +71 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +145 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/releases.d.ts +50 -0
- package/dist/releases.d.ts.map +1 -0
- package/dist/releases.js +113 -0
- package/dist/releases.js.map +1 -0
- package/package.json +39 -0
- package/src/auth/basic.ts +102 -0
- package/src/auth/oauth.ts +183 -0
- package/src/auth/token.ts +81 -0
- package/src/auth.ts +100 -0
- package/src/client.test.ts +115 -0
- package/src/client.ts +109 -0
- package/src/commits.ts +184 -0
- package/src/feedback.ts +166 -0
- package/src/index.ts +15 -0
- package/src/issues.ts +185 -0
- package/src/rate-limit.ts +210 -0
- package/src/releases.ts +149 -0
- package/test/auth/basic.test.ts +122 -0
- package/test/auth/oauth.test.ts +196 -0
- package/test/auth/token.test.ts +97 -0
- package/test/commits.test.ts +169 -0
- package/test/feedback.test.ts +203 -0
- package/test/issues.test.ts +197 -0
- package/test/rate-limit.test.ts +154 -0
- package/test/releases.test.ts +187 -0
- package/tsconfig.json +10 -0
- package/tsconfig.tsbuildinfo +1 -0
package/dist/feedback.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* False positive feedback listener for dependency tracking
|
|
3
|
+
* Monitors GitHub issue labels to collect user feedback on detections
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Listener that monitors issue labels for false positive feedback
|
|
7
|
+
*/
|
|
8
|
+
export class FeedbackListener {
|
|
9
|
+
issueManager;
|
|
10
|
+
truePositiveLabel;
|
|
11
|
+
falsePositiveLabel;
|
|
12
|
+
constructor(issueManager, config = {}) {
|
|
13
|
+
this.issueManager = issueManager;
|
|
14
|
+
this.truePositiveLabel = config.truePositiveLabel || 'true-positive';
|
|
15
|
+
this.falsePositiveLabel = config.falsePositiveLabel || 'false-positive';
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Collect feedback from issues with feedback labels
|
|
19
|
+
*/
|
|
20
|
+
async collectFeedback(options = {}) {
|
|
21
|
+
const issues = await this.issueManager.listIssues();
|
|
22
|
+
const truePositives = [];
|
|
23
|
+
const falsePositives = [];
|
|
24
|
+
for (const issue of issues) {
|
|
25
|
+
// Filter by date range if specified
|
|
26
|
+
if (options.startDate && issue.created_at) {
|
|
27
|
+
const issueDate = new Date(issue.created_at);
|
|
28
|
+
if (issueDate < options.startDate)
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (options.endDate && issue.created_at) {
|
|
32
|
+
const issueDate = new Date(issue.created_at);
|
|
33
|
+
if (issueDate > options.endDate)
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
// Filter by repository if specified
|
|
37
|
+
if (options.repository && issue.repository !== options.repository) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const labels = issue.labels || [];
|
|
41
|
+
const labelNames = labels.map((l) => typeof l === 'string' ? l : l.name);
|
|
42
|
+
const hasTrue = labelNames.includes(this.truePositiveLabel);
|
|
43
|
+
const hasFalse = labelNames.includes(this.falsePositiveLabel);
|
|
44
|
+
// Handle issues with both labels as a special case (log warning but count as true positive)
|
|
45
|
+
if (hasTrue && hasFalse) {
|
|
46
|
+
console.warn(`Issue #${issue.number} has both true-positive and false-positive labels. Counting as true-positive.`);
|
|
47
|
+
truePositives.push({
|
|
48
|
+
number: issue.number,
|
|
49
|
+
title: issue.title,
|
|
50
|
+
created_at: issue.created_at
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else if (hasTrue) {
|
|
54
|
+
truePositives.push({
|
|
55
|
+
number: issue.number,
|
|
56
|
+
title: issue.title,
|
|
57
|
+
created_at: issue.created_at
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
else if (hasFalse) {
|
|
61
|
+
falsePositives.push({
|
|
62
|
+
number: issue.number,
|
|
63
|
+
title: issue.title,
|
|
64
|
+
created_at: issue.created_at
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
truePositives,
|
|
70
|
+
falsePositives,
|
|
71
|
+
total: truePositives.length + falsePositives.length
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Calculate false positive rate from collected feedback
|
|
76
|
+
*/
|
|
77
|
+
async getFeedbackRate(options = {}) {
|
|
78
|
+
const feedback = await this.collectFeedback(options);
|
|
79
|
+
if (feedback.total === 0) {
|
|
80
|
+
return {
|
|
81
|
+
falsePositiveRate: 0,
|
|
82
|
+
truePositiveRate: 0,
|
|
83
|
+
totalFeedback: 0
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
falsePositiveRate: feedback.falsePositives.length / feedback.total,
|
|
88
|
+
truePositiveRate: feedback.truePositives.length / feedback.total,
|
|
89
|
+
totalFeedback: feedback.total
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get feedback from recent time window (e.g., last 30 days)
|
|
94
|
+
*/
|
|
95
|
+
async getRecentFeedback(days, referenceDate) {
|
|
96
|
+
const endDate = referenceDate || new Date();
|
|
97
|
+
const startDate = new Date(endDate);
|
|
98
|
+
startDate.setDate(startDate.getDate() - days);
|
|
99
|
+
return this.collectFeedback({ startDate, endDate });
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Check if a specific issue has feedback label
|
|
103
|
+
*/
|
|
104
|
+
async monitorIssue(issueNumber) {
|
|
105
|
+
const issue = await this.issueManager.getIssue(issueNumber);
|
|
106
|
+
const labels = issue.labels || [];
|
|
107
|
+
const labelNames = labels.map((l) => typeof l === 'string' ? l : l.name);
|
|
108
|
+
return (labelNames.includes(this.truePositiveLabel) || labelNames.includes(this.falsePositiveLabel));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=feedback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback.js","sourceRoot":"","sources":["../src/feedback.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAqCH;;GAEG;AACH,MAAM,OAAO,gBAAgB;IACnB,YAAY,CAAwB;IACpC,iBAAiB,CAAS;IAC1B,kBAAkB,CAAS;IAEnC,YAAY,YAAmC,EAAE,MAAM,GAAmB,EAAE,EAAE;QAC5E,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,iBAAiB,GAAG,MAAM,CAAC,iBAAiB,IAAI,eAAe,CAAC;QACrE,IAAI,CAAC,kBAAkB,GAAG,MAAM,CAAC,kBAAkB,IAAI,gBAAgB,CAAC;IAAA,CACzE;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,OAAO,GAAmB,EAAE,EAAyB;QACzE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC;QAEpD,MAAM,aAAa,GAAkC,EAAE,CAAC;QACxD,MAAM,cAAc,GAAmC,EAAE,CAAC;QAE1D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,oCAAoC;YACpC,IAAI,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;gBAC1C,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBAC7C,IAAI,SAAS,GAAG,OAAO,CAAC,SAAS;oBAAE,SAAS;YAC9C,CAAC;YACD,IAAI,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;gBACxC,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBAC7C,IAAI,SAAS,GAAG,OAAO,CAAC,OAAO;oBAAE,SAAS;YAC5C,CAAC;YAED,oCAAoC;YACpC,IAAI,OAAO,CAAC,UAAU,IAAK,KAAa,CAAC,UAAU,KAAK,OAAO,CAAC,UAAU,EAAE,CAAC;gBAC3E,SAAS;YACX,CAAC;YAED,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC;YAClC,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAA4B,EAAE,EAAE,CAC7D,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CACnC,CAAC;YAEF,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YAC5D,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;YAE9D,4FAA4F;YAC5F,IAAI,OAAO,IAAI,QAAQ,EAAE,CAAC;gBACxB,OAAO,CAAC,IAAI,CACV,UAAU,KAAK,CAAC,MAAM,+EAA+E,CACtG,CAAC;gBACF,aAAa,CAAC,IAAI,CAAC;oBACjB,MAAM,EAAE,KAAK,CAAC,MAAM;oBACpB,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,UAAU,EAAE,KAAK,CAAC,UAAU;iBAC7B,CAAC,CAAC;YACL,CAAC;iBAAM,IAAI,OAAO,EAAE,CAAC;gBACnB,aAAa,CAAC,IAAI,CAAC;oBACjB,MAAM,EAAE,KAAK,CAAC,MAAM;oBACpB,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,UAAU,EAAE,KAAK,CAAC,UAAU;iBAC7B,CAAC,CAAC;YACL,CAAC;iBAAM,IAAI,QAAQ,EAAE,CAAC;gBACpB,cAAc,CAAC,IAAI,CAAC;oBAClB,MAAM,EAAE,KAAK,CAAC,MAAM;oBACpB,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,UAAU,EAAE,KAAK,CAAC,UAAU;iBAC7B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO;YACL,aAAa;YACb,cAAc;YACd,KAAK,EAAE,aAAa,CAAC,MAAM,GAAG,cAAc,CAAC,MAAM;SACpD,CAAC;IAAA,CACH;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,OAAO,GAAmB,EAAE,EAAyB;QACzE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAErD,IAAI,QAAQ,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO;gBACL,iBAAiB,EAAE,CAAC;gBACpB,gBAAgB,EAAE,CAAC;gBACnB,aAAa,EAAE,CAAC;aACjB,CAAC;QACJ,CAAC;QAED,OAAO;YACL,iBAAiB,EAAE,QAAQ,CAAC,cAAc,CAAC,MAAM,GAAG,QAAQ,CAAC,KAAK;YAClE,gBAAgB,EAAE,QAAQ,CAAC,aAAa,CAAC,MAAM,GAAG,QAAQ,CAAC,KAAK;YAChE,aAAa,EAAE,QAAQ,CAAC,KAAK;SAC9B,CAAC;IAAA,CACH;IAED;;OAEG;IACH,KAAK,CAAC,iBAAiB,CAAC,IAAY,EAAE,aAAoB,EAAyB;QACjF,MAAM,OAAO,GAAG,aAAa,IAAI,IAAI,IAAI,EAAE,CAAC;QAC5C,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;QACpC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QAE9C,OAAO,IAAI,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;IAAA,CACrD;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAAC,WAAmB,EAAoB;QACxD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC;QAClC,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAA4B,EAAE,EAAE,CAC7D,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CACnC,CAAC;QAEF,OAAO,CACL,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAC5F,CAAC;IAAA,CACH;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @dependabit/github-client - GitHub API client wrapper
|
|
3
|
+
*/
|
|
4
|
+
export * from './client.js';
|
|
5
|
+
export * from './commits.js';
|
|
6
|
+
export { IssueManager } from './issues.js';
|
|
7
|
+
export type { IssueData, IssueResult, UpdateIssueData } from './issues.js';
|
|
8
|
+
export { ReleaseManager } from './releases.js';
|
|
9
|
+
export type { Release, ReleaseComparison } from './releases.js';
|
|
10
|
+
export { RateLimitHandler } from './rate-limit.js';
|
|
11
|
+
export type { RateLimitInfo, RateLimitStatus, BudgetReservation } from './rate-limit.js';
|
|
12
|
+
export * from './auth.js';
|
|
13
|
+
export { FeedbackListener } from './feedback.js';
|
|
14
|
+
export type { FeedbackConfig, FeedbackData, FeedbackRate } from './feedback.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,YAAY,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzF,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @dependabit/github-client - GitHub API client wrapper
|
|
3
|
+
*/
|
|
4
|
+
export * from './client.js';
|
|
5
|
+
export * from './commits.js';
|
|
6
|
+
export { IssueManager } from './issues.js';
|
|
7
|
+
export { ReleaseManager } from './releases.js';
|
|
8
|
+
export { RateLimitHandler } from './rate-limit.js';
|
|
9
|
+
export * from './auth.js';
|
|
10
|
+
export { FeedbackListener } from './feedback.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/issues.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue Manager
|
|
3
|
+
* Handles GitHub issue creation and management for dependency changes
|
|
4
|
+
*/
|
|
5
|
+
export interface IssueData {
|
|
6
|
+
owner: string;
|
|
7
|
+
repo: string;
|
|
8
|
+
title: string;
|
|
9
|
+
body: string;
|
|
10
|
+
severity: 'breaking' | 'major' | 'minor';
|
|
11
|
+
dependency: {
|
|
12
|
+
id: string;
|
|
13
|
+
url: string;
|
|
14
|
+
};
|
|
15
|
+
assignee?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface IssueResult {
|
|
18
|
+
number: number;
|
|
19
|
+
url: string;
|
|
20
|
+
labels: string[];
|
|
21
|
+
assignees?: string[] | undefined;
|
|
22
|
+
}
|
|
23
|
+
export interface UpdateIssueData {
|
|
24
|
+
owner: string;
|
|
25
|
+
repo: string;
|
|
26
|
+
issueNumber: number;
|
|
27
|
+
body: string;
|
|
28
|
+
severity?: 'breaking' | 'major' | 'minor';
|
|
29
|
+
append?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export declare class IssueManager {
|
|
32
|
+
private octokit;
|
|
33
|
+
constructor(auth?: string);
|
|
34
|
+
/**
|
|
35
|
+
* Creates a new issue for a dependency change
|
|
36
|
+
*/
|
|
37
|
+
createIssue(data: IssueData): Promise<IssueResult>;
|
|
38
|
+
/**
|
|
39
|
+
* Finds an existing issue for a dependency
|
|
40
|
+
*/
|
|
41
|
+
findExistingIssue(params: {
|
|
42
|
+
owner: string;
|
|
43
|
+
repo: string;
|
|
44
|
+
dependencyId: string;
|
|
45
|
+
}): Promise<IssueResult | null>;
|
|
46
|
+
/**
|
|
47
|
+
* Updates an existing issue
|
|
48
|
+
*/
|
|
49
|
+
updateIssue(data: UpdateIssueData): Promise<IssueResult>;
|
|
50
|
+
/**
|
|
51
|
+
* Formats the issue body with dependency metadata
|
|
52
|
+
*/
|
|
53
|
+
private formatIssueBody;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=issues.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issues.d.ts","sourceRoot":"","sources":["../src/issues.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;IACzC,UAAU,EAAE;QACV,EAAE,EAAE,MAAM,CAAC;QACX,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAU;IAEzB,YAAY,IAAI,CAAC,EAAE,MAAM,EAIxB;IAED;;OAEG;IACG,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,CAsBvD;IAED;;OAEG;IACG,iBAAiB,CAAC,MAAM,EAAE;QAC9B,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,MAAM,CAAC;KACtB,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA8B9B;IAED;;OAEG;IACG,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CAsD7D;IAED;;OAEG;IACH,OAAO,CAAC,eAAe;CAWxB"}
|
package/dist/issues.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Issue Manager
|
|
3
|
+
* Handles GitHub issue creation and management for dependency changes
|
|
4
|
+
*/
|
|
5
|
+
import { Octokit } from 'octokit';
|
|
6
|
+
export class IssueManager {
|
|
7
|
+
octokit;
|
|
8
|
+
constructor(auth) {
|
|
9
|
+
this.octokit = new Octokit({
|
|
10
|
+
auth: auth || process.env['GITHUB_TOKEN']
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates a new issue for a dependency change
|
|
15
|
+
*/
|
|
16
|
+
async createIssue(data) {
|
|
17
|
+
const { owner, repo, title, body, severity, dependency, assignee } = data;
|
|
18
|
+
// Prepare labels
|
|
19
|
+
const labels = ['dependabit', `severity:${severity}`, 'dependency-update'];
|
|
20
|
+
// Create the issue
|
|
21
|
+
const response = await this.octokit.rest.issues.create({
|
|
22
|
+
owner,
|
|
23
|
+
repo,
|
|
24
|
+
title,
|
|
25
|
+
body: this.formatIssueBody(body, dependency),
|
|
26
|
+
labels,
|
|
27
|
+
...(assignee && { assignees: [assignee] })
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
number: response.data.number,
|
|
31
|
+
url: response.data.html_url,
|
|
32
|
+
labels,
|
|
33
|
+
...(assignee && { assignees: [assignee] })
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Finds an existing issue for a dependency
|
|
38
|
+
*/
|
|
39
|
+
async findExistingIssue(params) {
|
|
40
|
+
const { owner, repo, dependencyId } = params;
|
|
41
|
+
try {
|
|
42
|
+
// Search for open issues with dependabit label and dependency ID
|
|
43
|
+
const query = `repo:${owner}/${repo} is:issue is:open label:dependabit ${dependencyId}`;
|
|
44
|
+
const response = await this.octokit.rest.search.issuesAndPullRequests({
|
|
45
|
+
q: query,
|
|
46
|
+
per_page: 1
|
|
47
|
+
});
|
|
48
|
+
if (response.data.items.length === 0) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const issue = response.data.items[0];
|
|
52
|
+
if (!issue) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
number: issue.number,
|
|
57
|
+
url: issue.html_url,
|
|
58
|
+
labels: issue.labels.map((l) => (typeof l === 'string' ? l : l.name || ''))
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error('Error finding existing issue:', error);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Updates an existing issue
|
|
68
|
+
*/
|
|
69
|
+
async updateIssue(data) {
|
|
70
|
+
const { owner, repo, issueNumber, body, severity, append } = data;
|
|
71
|
+
let finalBody = body;
|
|
72
|
+
// If appending, fetch current body first
|
|
73
|
+
if (append) {
|
|
74
|
+
const current = await this.octokit.rest.issues.get({
|
|
75
|
+
owner,
|
|
76
|
+
repo,
|
|
77
|
+
issue_number: issueNumber
|
|
78
|
+
});
|
|
79
|
+
finalBody = `${current.data.body}\n\n---\n\n${body}`;
|
|
80
|
+
}
|
|
81
|
+
// Update labels if severity changed
|
|
82
|
+
const updateParams = {
|
|
83
|
+
owner,
|
|
84
|
+
repo,
|
|
85
|
+
issue_number: issueNumber,
|
|
86
|
+
body: finalBody
|
|
87
|
+
};
|
|
88
|
+
if (severity) {
|
|
89
|
+
const current = await this.octokit.rest.issues.get({
|
|
90
|
+
owner,
|
|
91
|
+
repo,
|
|
92
|
+
issue_number: issueNumber
|
|
93
|
+
});
|
|
94
|
+
const existingLabels = current.data.labels
|
|
95
|
+
.map((l) => (typeof l === 'string' ? l : l.name))
|
|
96
|
+
.filter((l) => !!l);
|
|
97
|
+
const severityLabels = ['dependabit', `severity:${severity}`, 'dependency-update'];
|
|
98
|
+
const mergedLabels = Array.from(new Set([...existingLabels, ...severityLabels]));
|
|
99
|
+
updateParams.labels = mergedLabels;
|
|
100
|
+
}
|
|
101
|
+
const response = await this.octokit.rest.issues.update(updateParams);
|
|
102
|
+
return {
|
|
103
|
+
number: response.data.number,
|
|
104
|
+
url: response.data.html_url,
|
|
105
|
+
labels: response.data.labels.map((l) => (typeof l === 'string' ? l : l.name || ''))
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Formats the issue body with dependency metadata
|
|
110
|
+
*/
|
|
111
|
+
formatIssueBody(body, dependency) {
|
|
112
|
+
return `${body}
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
**Dependency Information**
|
|
117
|
+
- ID: \`${dependency.id}\`
|
|
118
|
+
- URL: ${dependency.url}
|
|
119
|
+
|
|
120
|
+
*This issue was automatically created by dependabit. Add \`false-positive\` or \`true-positive\` label to provide feedback.*`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=issues.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issues.js","sourceRoot":"","sources":["../src/issues.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate Limit Handler
|
|
3
|
+
* Manages GitHub API rate limits and request budgeting
|
|
4
|
+
*/
|
|
5
|
+
export interface RateLimitInfo {
|
|
6
|
+
limit: number;
|
|
7
|
+
remaining: number;
|
|
8
|
+
reset: Date;
|
|
9
|
+
used: number;
|
|
10
|
+
warning?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface RateLimitStatus {
|
|
13
|
+
core: RateLimitInfo & {
|
|
14
|
+
percentageRemaining: number;
|
|
15
|
+
};
|
|
16
|
+
search: RateLimitInfo & {
|
|
17
|
+
percentageRemaining: number;
|
|
18
|
+
};
|
|
19
|
+
graphql: RateLimitInfo & {
|
|
20
|
+
percentageRemaining: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface BudgetReservation {
|
|
24
|
+
reserved: boolean;
|
|
25
|
+
reason?: string;
|
|
26
|
+
waitTime?: number;
|
|
27
|
+
}
|
|
28
|
+
export declare class RateLimitHandler {
|
|
29
|
+
private octokit;
|
|
30
|
+
private lastCheck?;
|
|
31
|
+
private lastCheckTime?;
|
|
32
|
+
constructor(auth?: string);
|
|
33
|
+
/**
|
|
34
|
+
* Checks current rate limit status
|
|
35
|
+
*/
|
|
36
|
+
checkRateLimit(): Promise<RateLimitInfo>;
|
|
37
|
+
/**
|
|
38
|
+
* Waits if rate limited
|
|
39
|
+
*/
|
|
40
|
+
waitIfNeeded(): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Calculates wait time until rate limit resets
|
|
43
|
+
*/
|
|
44
|
+
calculateWaitTime(rateLimitInfo: RateLimitInfo): number;
|
|
45
|
+
/**
|
|
46
|
+
* Attempts to reserve API call budget with proactive checking
|
|
47
|
+
*/
|
|
48
|
+
reserveBudget(callsNeeded: number, options?: {
|
|
49
|
+
safetyMargin?: number;
|
|
50
|
+
maxWaitTime?: number;
|
|
51
|
+
}): Promise<BudgetReservation>;
|
|
52
|
+
/**
|
|
53
|
+
* Proactively check if operation can proceed without hitting rate limit
|
|
54
|
+
*/
|
|
55
|
+
canProceed(estimatedCalls: number, options?: {
|
|
56
|
+
threshold?: number;
|
|
57
|
+
safetyMargin?: number;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
canProceed: boolean;
|
|
60
|
+
reason?: string;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Gets detailed rate limit status for all API categories
|
|
64
|
+
*/
|
|
65
|
+
getRateLimitStatus(): Promise<RateLimitStatus>;
|
|
66
|
+
/**
|
|
67
|
+
* Gets cached rate limit status (avoids API call)
|
|
68
|
+
*/
|
|
69
|
+
getCachedStatus(): RateLimitStatus | undefined;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,IAAI,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,aAAa,GAAG;QAAE,mBAAmB,EAAE,MAAM,CAAA;KAAE,CAAC;IACtD,MAAM,EAAE,aAAa,GAAG;QAAE,mBAAmB,EAAE,MAAM,CAAA;KAAE,CAAC;IACxD,OAAO,EAAE,aAAa,GAAG;QAAE,mBAAmB,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1D;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,SAAS,CAAC,CAAkB;IACpC,OAAO,CAAC,aAAa,CAAC,CAAO;IAE7B,YAAY,IAAI,CAAC,EAAE,MAAM,EAIxB;IAED;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,aAAa,CAAC,CAiB7C;IAED;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAUlC;IAED;;OAEG;IACH,iBAAiB,CAAC,aAAa,EAAE,aAAa,GAAG,MAAM,CAUtD;IAED;;OAEG;IACG,aAAa,CACjB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;QACR,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,GACA,OAAO,CAAC,iBAAiB,CAAC,CA4B5B;IAED;;OAEG;IACG,UAAU,CACd,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE;QACR,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,GACA,OAAO,CAAC;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAwBnD;IAED;;OAEG;IACG,kBAAkB,IAAI,OAAO,CAAC,eAAe,CAAC,CA2BnD;IAED;;OAEG;IACH,eAAe,IAAI,eAAe,GAAG,SAAS,CAS7C;CACF"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate Limit Handler
|
|
3
|
+
* Manages GitHub API rate limits and request budgeting
|
|
4
|
+
*/
|
|
5
|
+
import { Octokit } from 'octokit';
|
|
6
|
+
export class RateLimitHandler {
|
|
7
|
+
octokit;
|
|
8
|
+
lastCheck;
|
|
9
|
+
lastCheckTime;
|
|
10
|
+
constructor(auth) {
|
|
11
|
+
this.octokit = new Octokit({
|
|
12
|
+
auth: auth || process.env['GITHUB_TOKEN']
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Checks current rate limit status
|
|
17
|
+
*/
|
|
18
|
+
async checkRateLimit() {
|
|
19
|
+
const response = await this.octokit.rest.rateLimit.get();
|
|
20
|
+
const { rate } = response.data;
|
|
21
|
+
const info = {
|
|
22
|
+
limit: rate.limit,
|
|
23
|
+
remaining: rate.remaining,
|
|
24
|
+
reset: new Date(rate.reset * 1000),
|
|
25
|
+
used: rate.used
|
|
26
|
+
};
|
|
27
|
+
// Add warning if approaching limit
|
|
28
|
+
if (info.remaining < info.limit * 0.1) {
|
|
29
|
+
info.warning = `Only ${info.remaining} requests remaining. Reset at ${info.reset.toISOString()}`;
|
|
30
|
+
}
|
|
31
|
+
return info;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Waits if rate limited
|
|
35
|
+
*/
|
|
36
|
+
async waitIfNeeded() {
|
|
37
|
+
const rateLimit = await this.checkRateLimit();
|
|
38
|
+
if (rateLimit.remaining === 0) {
|
|
39
|
+
const waitTime = this.calculateWaitTime(rateLimit);
|
|
40
|
+
if (waitTime > 0) {
|
|
41
|
+
console.log(`Rate limited. Waiting ${Math.ceil(waitTime / 1000)} seconds until reset...`);
|
|
42
|
+
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Calculates wait time until rate limit resets
|
|
48
|
+
*/
|
|
49
|
+
calculateWaitTime(rateLimitInfo) {
|
|
50
|
+
if (rateLimitInfo.remaining > 0) {
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
const now = Date.now();
|
|
54
|
+
const resetTime = rateLimitInfo.reset.getTime();
|
|
55
|
+
const waitTime = Math.max(0, resetTime - now);
|
|
56
|
+
return waitTime;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Attempts to reserve API call budget with proactive checking
|
|
60
|
+
*/
|
|
61
|
+
async reserveBudget(callsNeeded, options) {
|
|
62
|
+
const safetyMargin = options?.safetyMargin ?? Math.ceil(callsNeeded * 0.1);
|
|
63
|
+
const totalNeeded = callsNeeded + safetyMargin;
|
|
64
|
+
const rateLimit = await this.checkRateLimit();
|
|
65
|
+
if (rateLimit.remaining >= totalNeeded) {
|
|
66
|
+
return {
|
|
67
|
+
reserved: true
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const waitTime = this.calculateWaitTime(rateLimit);
|
|
71
|
+
// Check if wait time exceeds maximum allowed
|
|
72
|
+
if (options?.maxWaitTime && waitTime > options.maxWaitTime) {
|
|
73
|
+
return {
|
|
74
|
+
reserved: false,
|
|
75
|
+
reason: `Wait time (${Math.ceil(waitTime / 1000)}s) exceeds maximum (${Math.ceil(options.maxWaitTime / 1000)}s)`,
|
|
76
|
+
waitTime
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
reserved: false,
|
|
81
|
+
reason: `Insufficient API quota. Need ${callsNeeded} + ${safetyMargin} margin, have ${rateLimit.remaining}`,
|
|
82
|
+
waitTime
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Proactively check if operation can proceed without hitting rate limit
|
|
87
|
+
*/
|
|
88
|
+
async canProceed(estimatedCalls, options) {
|
|
89
|
+
const threshold = options?.threshold ?? 100;
|
|
90
|
+
const safetyMargin = options?.safetyMargin ?? Math.ceil(estimatedCalls * 0.1);
|
|
91
|
+
const totalNeeded = estimatedCalls + safetyMargin;
|
|
92
|
+
const rateLimit = await this.checkRateLimit();
|
|
93
|
+
// Check if we have enough remaining calls
|
|
94
|
+
if (rateLimit.remaining < totalNeeded) {
|
|
95
|
+
return {
|
|
96
|
+
canProceed: false,
|
|
97
|
+
reason: `Insufficient quota: need ${totalNeeded}, have ${rateLimit.remaining}`
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Check if we'd drop below threshold
|
|
101
|
+
if (rateLimit.remaining - totalNeeded < threshold) {
|
|
102
|
+
return {
|
|
103
|
+
canProceed: false,
|
|
104
|
+
reason: `Operation would leave only ${rateLimit.remaining - totalNeeded} calls (threshold: ${threshold})`
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return { canProceed: true };
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Gets detailed rate limit status for all API categories
|
|
111
|
+
*/
|
|
112
|
+
async getRateLimitStatus() {
|
|
113
|
+
const response = await this.octokit.rest.rateLimit.get();
|
|
114
|
+
const { resources } = response.data;
|
|
115
|
+
const createInfo = (resource) => ({
|
|
116
|
+
limit: resource.limit,
|
|
117
|
+
remaining: resource.remaining,
|
|
118
|
+
reset: new Date(resource.reset * 1000),
|
|
119
|
+
used: resource.used,
|
|
120
|
+
percentageRemaining: resource.limit > 0 ? (resource.remaining / resource.limit) * 100 : 0
|
|
121
|
+
});
|
|
122
|
+
const status = {
|
|
123
|
+
core: createInfo(resources.core),
|
|
124
|
+
search: createInfo(resources.search),
|
|
125
|
+
graphql: createInfo(resources.graphql || { limit: 0, remaining: 0, reset: 0, used: 0 })
|
|
126
|
+
};
|
|
127
|
+
this.lastCheck = status;
|
|
128
|
+
this.lastCheckTime = new Date();
|
|
129
|
+
return status;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Gets cached rate limit status (avoids API call)
|
|
133
|
+
*/
|
|
134
|
+
getCachedStatus() {
|
|
135
|
+
// Return cached status if less than 60 seconds old
|
|
136
|
+
if (this.lastCheck && this.lastCheckTime) {
|
|
137
|
+
const age = Date.now() - this.lastCheckTime.getTime();
|
|
138
|
+
if (age < 60000) {
|
|
139
|
+
return this.lastCheck;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=rate-limit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Release Manager
|
|
3
|
+
* Handles fetching and comparing GitHub releases
|
|
4
|
+
*/
|
|
5
|
+
export interface Release {
|
|
6
|
+
tagName: string;
|
|
7
|
+
name: string;
|
|
8
|
+
publishedAt: Date;
|
|
9
|
+
body?: string | undefined;
|
|
10
|
+
htmlUrl: string;
|
|
11
|
+
prerelease?: boolean;
|
|
12
|
+
draft?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface ReleaseComparison {
|
|
15
|
+
newReleases: Release[];
|
|
16
|
+
oldReleases: Release[];
|
|
17
|
+
}
|
|
18
|
+
export declare class ReleaseManager {
|
|
19
|
+
private octokit;
|
|
20
|
+
constructor(auth?: string);
|
|
21
|
+
/**
|
|
22
|
+
* Fetches the latest release from a repository
|
|
23
|
+
*/
|
|
24
|
+
getLatestRelease(params: {
|
|
25
|
+
owner: string;
|
|
26
|
+
repo: string;
|
|
27
|
+
}): Promise<Release | null>;
|
|
28
|
+
/**
|
|
29
|
+
* Fetches all releases from a repository
|
|
30
|
+
*/
|
|
31
|
+
getAllReleases(params: {
|
|
32
|
+
owner: string;
|
|
33
|
+
repo: string;
|
|
34
|
+
page?: number;
|
|
35
|
+
perPage?: number;
|
|
36
|
+
}): Promise<Release[]>;
|
|
37
|
+
/**
|
|
38
|
+
* Compares two sets of releases to find new ones
|
|
39
|
+
*/
|
|
40
|
+
compareReleases(oldReleases: Release[], newReleases: Release[]): ReleaseComparison;
|
|
41
|
+
/**
|
|
42
|
+
* Fetches release notes for a specific tag
|
|
43
|
+
*/
|
|
44
|
+
getReleaseByTag(params: {
|
|
45
|
+
owner: string;
|
|
46
|
+
repo: string;
|
|
47
|
+
tag: string;
|
|
48
|
+
}): Promise<Release | null>;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=releases.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"releases.d.ts","sourceRoot":"","sources":["../src/releases.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,IAAI,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,OAAO,EAAE,CAAC;IACvB,WAAW,EAAE,OAAO,EAAE,CAAC;CACxB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAU;IAEzB,YAAY,IAAI,CAAC,EAAE,MAAM,EAIxB;IAED;;OAEG;IACG,gBAAgB,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAwBvF;IAED;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE;QAC3B,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CA0BrB;IAED;;OAEG;IACH,eAAe,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,iBAAiB,CAcjF;IAED;;OAEG;IACG,eAAe,CAAC,MAAM,EAAE;QAC5B,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;KACb,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAyB1B;CACF"}
|