@lizardbyte/contribkit 2025.922.2626 → 2025.1130.1103
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 +40 -3
- package/dist/chunks/index.mjs +6 -0
- package/dist/chunks/index3.mjs +36 -36
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +32 -1
- package/dist/index.mjs +1 -1
- package/dist/shared/{contribkit.DAlakhwL.mjs → contribkit.DLdCPKip.mjs} +252 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# ContribKit
|
|
2
2
|
|
|
3
3
|
[](https://github.com/LizardByte/contribkit)
|
|
4
|
-
[](https://img.shields.io/github/actions/workflow/status/lizardbyte/contribkit/_ci-node.yml.svg?branch=master&label=CI%20build&logo=github&style=for-the-badge)](https://github.com/LizardByte/contribkit/actions/workflows/CI.yml?query=branch%3Amaster)
|
|
5
5
|
[](https://codecov.io/gh/LizardByte/contribkit)
|
|
6
6
|
[](https://www.npmjs.com/package/@lizardbyte/contribkit)
|
|
7
7
|
[](https://www.npmjs.com/package/@lizardbyte/contribkit)
|
|
@@ -13,7 +13,8 @@ Supports:
|
|
|
13
13
|
|
|
14
14
|
- Contributors:
|
|
15
15
|
- [**CrowdIn**](https://crowdin.com)
|
|
16
|
-
- [**GitHub**](https://github.com)
|
|
16
|
+
- [**GitHub Contributors**](https://github.com) (contributors to a specific repository)
|
|
17
|
+
- [**GitHub Contributions**](https://github.com) (merged PRs aggregated by repository owner across all repos for a single user)
|
|
17
18
|
- [**Gitlab**](https://gitlab.com)
|
|
18
19
|
- Sponsors:
|
|
19
20
|
- [**GitHub Sponsors**](https://github.com/sponsors)
|
|
@@ -37,11 +38,25 @@ CONTRIBKIT_CROWDIN_MIN_TRANSLATIONS=1
|
|
|
37
38
|
|
|
38
39
|
; GitHubContributors provider.
|
|
39
40
|
; Token requires the `public_repo` and `read:user` scopes.
|
|
41
|
+
; This provider tracks all contributors to a specific repository.
|
|
40
42
|
CONTRIBKIT_GITHUB_CONTRIBUTORS_TOKEN=
|
|
41
43
|
CONTRIBKIT_GITHUB_CONTRIBUTORS_LOGIN=
|
|
42
44
|
CONTRIBKIT_GITHUB_CONTRIBUTORS_MIN=1
|
|
43
45
|
CONTRIBKIT_GITHUB_CONTRIBUTORS_REPO=
|
|
44
46
|
|
|
47
|
+
; GitHubContributions provider.
|
|
48
|
+
; Token requires the `read:user` scope.
|
|
49
|
+
; This provider aggregates merged pull requests across all repositories by repository owner (user or organization).
|
|
50
|
+
; Each owner appears once with the total merged PRs you authored to their repos.
|
|
51
|
+
; Avatar and link point to the owner (or to the repo if only one repo per owner).
|
|
52
|
+
; Only merged PRs are counted - open or closed-without-merge PRs are excluded.
|
|
53
|
+
CONTRIBKIT_GITHUB_CONTRIBUTIONS_TOKEN=
|
|
54
|
+
CONTRIBKIT_GITHUB_CONTRIBUTIONS_LOGIN=
|
|
55
|
+
; Optional: Cap the maximum contribution count per org/user (useful for circles visualization)
|
|
56
|
+
CONTRIBKIT_GITHUB_CONTRIBUTIONS_MAX=
|
|
57
|
+
; Optional: Apply logarithmic scaling to reduce dominance of high contributors (true/false)
|
|
58
|
+
CONTRIBKIT_GITHUB_CONTRIBUTIONS_LOGARITHMIC=
|
|
59
|
+
|
|
45
60
|
; GitlabContributors provider.
|
|
46
61
|
; Token requires the `read_api` and `read_user` scopes.
|
|
47
62
|
CONTRIBKIT_GITLAB_CONTRIBUTORS_TOKEN=
|
|
@@ -96,9 +111,26 @@ CONTRIBKIT_LIBERAPAY_LOGIN=
|
|
|
96
111
|
> This will require different env variables to be set for each provider, and to be created from separate
|
|
97
112
|
> commands.
|
|
98
113
|
|
|
114
|
+
#### GitHub Provider Options
|
|
115
|
+
|
|
116
|
+
There are two GitHub contributor providers available:
|
|
117
|
+
|
|
118
|
+
- **GitHubContributors**: Tracks all contributors to a specific repository (e.g., `owner/repo`). Each contributor appears once with their actual contribution count to that repository.
|
|
119
|
+
- **GitHubContributions**: Aggregates a single user's **merged pull requests** across all repositories, grouped by repository owner (user or organization). Each owner appears once with the total merged PRs. The avatar and link point to the owner (or to the specific repo if only one repo per owner).
|
|
120
|
+
|
|
121
|
+
Use **GitHubContributors** when you want to showcase everyone who has contributed to your project with their contribution counts.
|
|
122
|
+
Use **GitHubContributions** when you want to understand where a single user's completed contributions (merged PRs) have gone, without overwhelming duplicates per repo under the same owner.
|
|
123
|
+
|
|
124
|
+
**GitHubContributions accuracy**:
|
|
125
|
+
- Counts only **merged** pull requests - open or closed-without-merge PRs are excluded
|
|
126
|
+
- Discovers repos via **2 sources**:
|
|
127
|
+
1. **contributionsCollection** - Yearly commit timeline (full history) for discovering repositories you have committed to
|
|
128
|
+
2. **Search API** - Repositories where you have merged PRs (`is:pr is:merged author:login`)
|
|
129
|
+
- When an owner has only one repo, the link points to that repo; otherwise to the owner profile
|
|
130
|
+
|
|
99
131
|
Run:
|
|
100
132
|
|
|
101
|
-
```
|
|
133
|
+
```bash
|
|
102
134
|
npx contribkit
|
|
103
135
|
```
|
|
104
136
|
|
|
@@ -133,6 +165,11 @@ export default defineConfig({
|
|
|
133
165
|
// ...
|
|
134
166
|
},
|
|
135
167
|
|
|
168
|
+
// For contributor providers:
|
|
169
|
+
githubContributions: {
|
|
170
|
+
login: 'username',
|
|
171
|
+
},
|
|
172
|
+
|
|
136
173
|
// Rendering configs
|
|
137
174
|
width: 800,
|
|
138
175
|
renderer: 'tiers', // or 'circles'
|
package/dist/chunks/index.mjs
CHANGED
package/dist/chunks/index3.mjs
CHANGED
|
@@ -1514,7 +1514,7 @@ var hasRequiredLodash_groupby;
|
|
|
1514
1514
|
function requireLodash_groupby () {
|
|
1515
1515
|
if (hasRequiredLodash_groupby) return lodash_groupby.exports;
|
|
1516
1516
|
hasRequiredLodash_groupby = 1;
|
|
1517
|
-
(function (module, exports) {
|
|
1517
|
+
(function (module, exports$1) {
|
|
1518
1518
|
/** Used as the size to enable large array optimizations. */
|
|
1519
1519
|
var LARGE_ARRAY_SIZE = 200;
|
|
1520
1520
|
|
|
@@ -1608,7 +1608,7 @@ function requireLodash_groupby () {
|
|
|
1608
1608
|
var root = freeGlobal || freeSelf || Function('return this')();
|
|
1609
1609
|
|
|
1610
1610
|
/** Detect free variable `exports`. */
|
|
1611
|
-
var freeExports = exports && !exports.nodeType && exports;
|
|
1611
|
+
var freeExports = exports$1 && !exports$1.nodeType && exports$1;
|
|
1612
1612
|
|
|
1613
1613
|
/** Detect free variable `module`. */
|
|
1614
1614
|
var freeModule = freeExports && 'object' == 'object' && module && !module.nodeType && module;
|
|
@@ -4007,13 +4007,13 @@ var hasRequiredTransforms;
|
|
|
4007
4007
|
function requireTransforms () {
|
|
4008
4008
|
if (hasRequiredTransforms) return transforms;
|
|
4009
4009
|
hasRequiredTransforms = 1;
|
|
4010
|
-
(function (exports) {
|
|
4011
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4012
|
-
exports.HeaderTransformer = exports.RowTransformerValidator = void 0;
|
|
4010
|
+
(function (exports$1) {
|
|
4011
|
+
Object.defineProperty(exports$1, "__esModule", { value: true });
|
|
4012
|
+
exports$1.HeaderTransformer = exports$1.RowTransformerValidator = void 0;
|
|
4013
4013
|
var RowTransformerValidator_1 = requireRowTransformerValidator();
|
|
4014
|
-
Object.defineProperty(exports, "RowTransformerValidator", { enumerable: true, get: function () { return RowTransformerValidator_1.RowTransformerValidator; } });
|
|
4014
|
+
Object.defineProperty(exports$1, "RowTransformerValidator", { enumerable: true, get: function () { return RowTransformerValidator_1.RowTransformerValidator; } });
|
|
4015
4015
|
var HeaderTransformer_1 = requireHeaderTransformer();
|
|
4016
|
-
Object.defineProperty(exports, "HeaderTransformer", { enumerable: true, get: function () { return HeaderTransformer_1.HeaderTransformer; } });
|
|
4016
|
+
Object.defineProperty(exports$1, "HeaderTransformer", { enumerable: true, get: function () { return HeaderTransformer_1.HeaderTransformer; } });
|
|
4017
4017
|
|
|
4018
4018
|
} (transforms));
|
|
4019
4019
|
return transforms;
|
|
@@ -4392,17 +4392,17 @@ var hasRequiredColumn;
|
|
|
4392
4392
|
function requireColumn () {
|
|
4393
4393
|
if (hasRequiredColumn) return column;
|
|
4394
4394
|
hasRequiredColumn = 1;
|
|
4395
|
-
(function (exports) {
|
|
4396
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4397
|
-
exports.ColumnFormatter = exports.QuotedColumnParser = exports.NonQuotedColumnParser = exports.ColumnParser = void 0;
|
|
4395
|
+
(function (exports$1) {
|
|
4396
|
+
Object.defineProperty(exports$1, "__esModule", { value: true });
|
|
4397
|
+
exports$1.ColumnFormatter = exports$1.QuotedColumnParser = exports$1.NonQuotedColumnParser = exports$1.ColumnParser = void 0;
|
|
4398
4398
|
var ColumnParser_1 = requireColumnParser();
|
|
4399
|
-
Object.defineProperty(exports, "ColumnParser", { enumerable: true, get: function () { return ColumnParser_1.ColumnParser; } });
|
|
4399
|
+
Object.defineProperty(exports$1, "ColumnParser", { enumerable: true, get: function () { return ColumnParser_1.ColumnParser; } });
|
|
4400
4400
|
var NonQuotedColumnParser_1 = requireNonQuotedColumnParser();
|
|
4401
|
-
Object.defineProperty(exports, "NonQuotedColumnParser", { enumerable: true, get: function () { return NonQuotedColumnParser_1.NonQuotedColumnParser; } });
|
|
4401
|
+
Object.defineProperty(exports$1, "NonQuotedColumnParser", { enumerable: true, get: function () { return NonQuotedColumnParser_1.NonQuotedColumnParser; } });
|
|
4402
4402
|
var QuotedColumnParser_1 = requireQuotedColumnParser();
|
|
4403
|
-
Object.defineProperty(exports, "QuotedColumnParser", { enumerable: true, get: function () { return QuotedColumnParser_1.QuotedColumnParser; } });
|
|
4403
|
+
Object.defineProperty(exports$1, "QuotedColumnParser", { enumerable: true, get: function () { return QuotedColumnParser_1.QuotedColumnParser; } });
|
|
4404
4404
|
var ColumnFormatter_1 = requireColumnFormatter();
|
|
4405
|
-
Object.defineProperty(exports, "ColumnFormatter", { enumerable: true, get: function () { return ColumnFormatter_1.ColumnFormatter; } });
|
|
4405
|
+
Object.defineProperty(exports$1, "ColumnFormatter", { enumerable: true, get: function () { return ColumnFormatter_1.ColumnFormatter; } });
|
|
4406
4406
|
|
|
4407
4407
|
} (column));
|
|
4408
4408
|
return column;
|
|
@@ -4583,21 +4583,21 @@ var hasRequiredParser;
|
|
|
4583
4583
|
function requireParser () {
|
|
4584
4584
|
if (hasRequiredParser) return parser;
|
|
4585
4585
|
hasRequiredParser = 1;
|
|
4586
|
-
(function (exports) {
|
|
4587
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4588
|
-
exports.QuotedColumnParser = exports.NonQuotedColumnParser = exports.ColumnParser = exports.Token = exports.Scanner = exports.RowParser = exports.Parser = void 0;
|
|
4586
|
+
(function (exports$1) {
|
|
4587
|
+
Object.defineProperty(exports$1, "__esModule", { value: true });
|
|
4588
|
+
exports$1.QuotedColumnParser = exports$1.NonQuotedColumnParser = exports$1.ColumnParser = exports$1.Token = exports$1.Scanner = exports$1.RowParser = exports$1.Parser = void 0;
|
|
4589
4589
|
var Parser_1 = requireParser$1();
|
|
4590
|
-
Object.defineProperty(exports, "Parser", { enumerable: true, get: function () { return Parser_1.Parser; } });
|
|
4590
|
+
Object.defineProperty(exports$1, "Parser", { enumerable: true, get: function () { return Parser_1.Parser; } });
|
|
4591
4591
|
var RowParser_1 = requireRowParser();
|
|
4592
|
-
Object.defineProperty(exports, "RowParser", { enumerable: true, get: function () { return RowParser_1.RowParser; } });
|
|
4592
|
+
Object.defineProperty(exports$1, "RowParser", { enumerable: true, get: function () { return RowParser_1.RowParser; } });
|
|
4593
4593
|
var Scanner_1 = requireScanner();
|
|
4594
|
-
Object.defineProperty(exports, "Scanner", { enumerable: true, get: function () { return Scanner_1.Scanner; } });
|
|
4594
|
+
Object.defineProperty(exports$1, "Scanner", { enumerable: true, get: function () { return Scanner_1.Scanner; } });
|
|
4595
4595
|
var Token_1 = requireToken();
|
|
4596
|
-
Object.defineProperty(exports, "Token", { enumerable: true, get: function () { return Token_1.Token; } });
|
|
4596
|
+
Object.defineProperty(exports$1, "Token", { enumerable: true, get: function () { return Token_1.Token; } });
|
|
4597
4597
|
var column_1 = requireColumn();
|
|
4598
|
-
Object.defineProperty(exports, "ColumnParser", { enumerable: true, get: function () { return column_1.ColumnParser; } });
|
|
4599
|
-
Object.defineProperty(exports, "NonQuotedColumnParser", { enumerable: true, get: function () { return column_1.NonQuotedColumnParser; } });
|
|
4600
|
-
Object.defineProperty(exports, "QuotedColumnParser", { enumerable: true, get: function () { return column_1.QuotedColumnParser; } });
|
|
4598
|
+
Object.defineProperty(exports$1, "ColumnParser", { enumerable: true, get: function () { return column_1.ColumnParser; } });
|
|
4599
|
+
Object.defineProperty(exports$1, "NonQuotedColumnParser", { enumerable: true, get: function () { return column_1.NonQuotedColumnParser; } });
|
|
4600
|
+
Object.defineProperty(exports$1, "QuotedColumnParser", { enumerable: true, get: function () { return column_1.QuotedColumnParser; } });
|
|
4601
4601
|
|
|
4602
4602
|
} (parser));
|
|
4603
4603
|
return parser;
|
|
@@ -4834,7 +4834,7 @@ var hasRequiredSrc;
|
|
|
4834
4834
|
function requireSrc () {
|
|
4835
4835
|
if (hasRequiredSrc) return src;
|
|
4836
4836
|
hasRequiredSrc = 1;
|
|
4837
|
-
(function (exports) {
|
|
4837
|
+
(function (exports$1) {
|
|
4838
4838
|
var __createBinding = (src && src.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4839
4839
|
if (k2 === undefined) k2 = k;
|
|
4840
4840
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -4858,39 +4858,39 @@ function requireSrc () {
|
|
|
4858
4858
|
__setModuleDefault(result, mod);
|
|
4859
4859
|
return result;
|
|
4860
4860
|
};
|
|
4861
|
-
var __exportStar = (src && src.__exportStar) || function(m, exports) {
|
|
4862
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
4861
|
+
var __exportStar = (src && src.__exportStar) || function(m, exports$1) {
|
|
4862
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports$1, p)) __createBinding(exports$1, m, p);
|
|
4863
4863
|
};
|
|
4864
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4865
|
-
exports.parseString = exports.parseFile = exports.parseStream = exports.parse = exports.ParserOptions = exports.CsvParserStream = void 0;
|
|
4864
|
+
Object.defineProperty(exports$1, "__esModule", { value: true });
|
|
4865
|
+
exports$1.parseString = exports$1.parseFile = exports$1.parseStream = exports$1.parse = exports$1.ParserOptions = exports$1.CsvParserStream = void 0;
|
|
4866
4866
|
const fs = __importStar(require$$0$1);
|
|
4867
4867
|
const stream_1 = require$$1;
|
|
4868
4868
|
const ParserOptions_1 = requireParserOptions();
|
|
4869
4869
|
const CsvParserStream_1 = requireCsvParserStream();
|
|
4870
|
-
__exportStar(requireTypes(), exports);
|
|
4870
|
+
__exportStar(requireTypes(), exports$1);
|
|
4871
4871
|
var CsvParserStream_2 = requireCsvParserStream();
|
|
4872
|
-
Object.defineProperty(exports, "CsvParserStream", { enumerable: true, get: function () { return CsvParserStream_2.CsvParserStream; } });
|
|
4872
|
+
Object.defineProperty(exports$1, "CsvParserStream", { enumerable: true, get: function () { return CsvParserStream_2.CsvParserStream; } });
|
|
4873
4873
|
var ParserOptions_2 = requireParserOptions();
|
|
4874
|
-
Object.defineProperty(exports, "ParserOptions", { enumerable: true, get: function () { return ParserOptions_2.ParserOptions; } });
|
|
4874
|
+
Object.defineProperty(exports$1, "ParserOptions", { enumerable: true, get: function () { return ParserOptions_2.ParserOptions; } });
|
|
4875
4875
|
const parse = (args) => {
|
|
4876
4876
|
return new CsvParserStream_1.CsvParserStream(new ParserOptions_1.ParserOptions(args));
|
|
4877
4877
|
};
|
|
4878
|
-
exports.parse = parse;
|
|
4878
|
+
exports$1.parse = parse;
|
|
4879
4879
|
const parseStream = (stream, options) => {
|
|
4880
4880
|
return stream.pipe(new CsvParserStream_1.CsvParserStream(new ParserOptions_1.ParserOptions(options)));
|
|
4881
4881
|
};
|
|
4882
|
-
exports.parseStream = parseStream;
|
|
4882
|
+
exports$1.parseStream = parseStream;
|
|
4883
4883
|
const parseFile = (location, options = {}) => {
|
|
4884
4884
|
return fs.createReadStream(location).pipe(new CsvParserStream_1.CsvParserStream(new ParserOptions_1.ParserOptions(options)));
|
|
4885
4885
|
};
|
|
4886
|
-
exports.parseFile = parseFile;
|
|
4886
|
+
exports$1.parseFile = parseFile;
|
|
4887
4887
|
const parseString = (string, options) => {
|
|
4888
4888
|
const rs = new stream_1.Readable();
|
|
4889
4889
|
rs.push(string);
|
|
4890
4890
|
rs.push(null);
|
|
4891
4891
|
return rs.pipe(new CsvParserStream_1.CsvParserStream(new ParserOptions_1.ParserOptions(options)));
|
|
4892
4892
|
};
|
|
4893
|
-
exports.parseString = parseString;
|
|
4893
|
+
exports$1.parseString = parseString;
|
|
4894
4894
|
|
|
4895
4895
|
} (src));
|
|
4896
4896
|
return src;
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import cac from 'cac';
|
|
2
|
-
import { S as SvgComposer, i as generateBadge, p as partitionTiers, t as tierPresets, v as version, l as loadConfig, k as resolveProviders, j as guessProviders, r as resolveAvatars, q as outputFormats, s as svgToPng, g as svgToWebp } from './shared/contribkit.
|
|
2
|
+
import { S as SvgComposer, i as generateBadge, p as partitionTiers, t as tierPresets, v as version, l as loadConfig, k as resolveProviders, j as guessProviders, r as resolveAvatars, q as outputFormats, s as svgToPng, g as svgToWebp } from './shared/contribkit.DLdCPKip.mjs';
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import fsp from 'node:fs/promises';
|
|
5
5
|
import { resolve, dirname, relative, join } from 'node:path';
|
package/dist/index.d.mts
CHANGED
|
@@ -83,7 +83,7 @@ interface Sponsorship {
|
|
|
83
83
|
}
|
|
84
84
|
declare const outputFormats: readonly ["svg", "png", "webp", "json"];
|
|
85
85
|
type OutputFormat = typeof outputFormats[number];
|
|
86
|
-
type ProviderName = 'github' | 'patreon' | 'opencollective' | 'afdian' | 'polar' | 'liberapay' | 'githubContributors' | 'gitlabContributors' | 'crowdinContributors';
|
|
86
|
+
type ProviderName = 'github' | 'patreon' | 'opencollective' | 'afdian' | 'polar' | 'liberapay' | 'githubContributors' | 'gitlabContributors' | 'crowdinContributors' | 'githubContributions';
|
|
87
87
|
type GitHubAccountType = 'user' | 'organization';
|
|
88
88
|
interface ProvidersConfig {
|
|
89
89
|
github?: {
|
|
@@ -278,6 +278,36 @@ interface ProvidersConfig {
|
|
|
278
278
|
*/
|
|
279
279
|
minTranslations?: number;
|
|
280
280
|
};
|
|
281
|
+
githubContributions?: {
|
|
282
|
+
/**
|
|
283
|
+
* GitHub user login to fetch contributions for.
|
|
284
|
+
*
|
|
285
|
+
* Will read from `CONTRIBKIT_GITHUB_CONTRIBUTIONS_LOGIN` environment variable if not set.
|
|
286
|
+
*/
|
|
287
|
+
login?: string;
|
|
288
|
+
/**
|
|
289
|
+
* GitHub Token that has access to read user contributions.
|
|
290
|
+
*
|
|
291
|
+
* Will read from `CONTRIBKIT_GITHUB_CONTRIBUTIONS_TOKEN` environment variable if not set.
|
|
292
|
+
*
|
|
293
|
+
* @deprecated It's not recommended set this value directly, pass from env or use `.env` file.
|
|
294
|
+
*/
|
|
295
|
+
token?: string;
|
|
296
|
+
/**
|
|
297
|
+
* Cap the maximum contribution count per organization/user.
|
|
298
|
+
* Useful to prevent one dominant contributor from overshadowing others in visualizations.
|
|
299
|
+
*
|
|
300
|
+
* @example 100 // Cap all contributions at 100 PRs max
|
|
301
|
+
*/
|
|
302
|
+
maxContributions?: number;
|
|
303
|
+
/**
|
|
304
|
+
* Apply logarithmic scaling to contribution counts.
|
|
305
|
+
* Useful to reduce the visual dominance of high contributors while maintaining relative differences.
|
|
306
|
+
*
|
|
307
|
+
* @default false
|
|
308
|
+
*/
|
|
309
|
+
logarithmicScaling?: boolean;
|
|
310
|
+
};
|
|
281
311
|
}
|
|
282
312
|
interface ContribkitRenderOptions {
|
|
283
313
|
/**
|
|
@@ -557,6 +587,7 @@ declare const ProvidersMap: {
|
|
|
557
587
|
polar: Provider;
|
|
558
588
|
liberapay: Provider;
|
|
559
589
|
githubContributors: Provider;
|
|
590
|
+
githubContributions: Provider;
|
|
560
591
|
gitlabContributors: Provider;
|
|
561
592
|
crowdinContributors: Provider;
|
|
562
593
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { F as FALLBACK_AVATAR, G as GitHubProvider, P as ProvidersMap, S as SvgComposer, c as defaultConfig, b as defaultInlineCSS, a as defaultTiers, d as defineConfig, n as fetchGitHubSponsors, m as fetchSponsors, h as genSvgImage, i as generateBadge, j as guessProviders, l as loadConfig, o as makeQuery, q as outputFormats, p as partitionTiers, e as presets, f as resizeImage, r as resolveAvatars, k as resolveProviders, s as svgToPng, g as svgToWebp, t as tierPresets } from './shared/contribkit.
|
|
1
|
+
export { F as FALLBACK_AVATAR, G as GitHubProvider, P as ProvidersMap, S as SvgComposer, c as defaultConfig, b as defaultInlineCSS, a as defaultTiers, d as defineConfig, n as fetchGitHubSponsors, m as fetchSponsors, h as genSvgImage, i as generateBadge, j as guessProviders, l as loadConfig, o as makeQuery, q as outputFormats, p as partitionTiers, e as presets, f as resizeImage, r as resolveAvatars, k as resolveProviders, s as svgToPng, g as svgToWebp, t as tierPresets } from './shared/contribkit.DLdCPKip.mjs';
|
|
2
2
|
import 'unconfig';
|
|
3
3
|
import 'node:process';
|
|
4
4
|
import 'dotenv';
|
|
@@ -196,12 +196,18 @@ function loadEnv() {
|
|
|
196
196
|
token: process.env.CONTRIBKIT_CROWDIN_TOKEN,
|
|
197
197
|
projectId: Number(process.env.CONTRIBKIT_CROWDIN_PROJECT_ID),
|
|
198
198
|
minTranslations: Number(process.env.CONTRIBKIT_CROWDIN_MIN_TRANSLATIONS) || 1
|
|
199
|
+
},
|
|
200
|
+
githubContributions: {
|
|
201
|
+
login: process.env.CONTRIBKIT_GITHUB_CONTRIBUTIONS_LOGIN,
|
|
202
|
+
token: process.env.CONTRIBKIT_GITHUB_CONTRIBUTIONS_TOKEN,
|
|
203
|
+
maxContributions: Number(process.env.CONTRIBKIT_GITHUB_CONTRIBUTIONS_MAX) || void 0,
|
|
204
|
+
logarithmicScaling: process.env.CONTRIBKIT_GITHUB_CONTRIBUTIONS_LOGARITHMIC === "true"
|
|
199
205
|
}
|
|
200
206
|
};
|
|
201
207
|
return JSON.parse(JSON.stringify(config));
|
|
202
208
|
}
|
|
203
209
|
|
|
204
|
-
const version = "2025.
|
|
210
|
+
const version = "2025.1130.1103";
|
|
205
211
|
|
|
206
212
|
async function fetchImage(url) {
|
|
207
213
|
const arrayBuffer = await $fetch(url, {
|
|
@@ -345,7 +351,8 @@ function partitionTiers(sponsors, tiers, includePastSponsors) {
|
|
|
345
351
|
}
|
|
346
352
|
|
|
347
353
|
function genSvgImage(x, y, size, radius, base64Image, imageFormat) {
|
|
348
|
-
const
|
|
354
|
+
const hashInput = `${x}:${y}:${size}:${radius}:${base64Image}`;
|
|
355
|
+
const cropId = `c${crypto.createHash("sha256").update(hashInput).digest("hex").slice(0, 6)}`;
|
|
349
356
|
return `
|
|
350
357
|
<clipPath id="${cropId}">
|
|
351
358
|
<rect x="${x}" y="${y}" width="${size}" height="${size}" rx="${size * radius}" ry="${size * radius}" />
|
|
@@ -1105,6 +1112,246 @@ async function fetchGitHubContributors(token, login, repo, minContributions = 1)
|
|
|
1105
1112
|
}));
|
|
1106
1113
|
}
|
|
1107
1114
|
|
|
1115
|
+
const GitHubContributionsProvider = {
|
|
1116
|
+
name: "githubContributions",
|
|
1117
|
+
fetchSponsors(config) {
|
|
1118
|
+
if (!config.githubContributions?.login)
|
|
1119
|
+
throw new Error("GitHub login is required for githubContributions provider");
|
|
1120
|
+
return fetchGitHubContributions(
|
|
1121
|
+
config.githubContributions?.token || config.token,
|
|
1122
|
+
config.githubContributions.login,
|
|
1123
|
+
config.githubContributions.maxContributions,
|
|
1124
|
+
config.githubContributions.logarithmicScaling
|
|
1125
|
+
);
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
function createGraphQLFetch(token) {
|
|
1129
|
+
return async (body) => {
|
|
1130
|
+
return await $fetch("https://api.github.com/graphql", {
|
|
1131
|
+
method: "POST",
|
|
1132
|
+
headers: {
|
|
1133
|
+
Authorization: `bearer ${token}`,
|
|
1134
|
+
"Content-Type": "application/json"
|
|
1135
|
+
},
|
|
1136
|
+
body
|
|
1137
|
+
});
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
async function fetchUserCreationDate(graphqlFetch, login) {
|
|
1141
|
+
const userInfoQuery = `
|
|
1142
|
+
query($login: String!) {
|
|
1143
|
+
user(login: $login) {
|
|
1144
|
+
createdAt
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
`;
|
|
1148
|
+
const userInfo = await graphqlFetch({
|
|
1149
|
+
query: userInfoQuery,
|
|
1150
|
+
variables: { login }
|
|
1151
|
+
});
|
|
1152
|
+
return new Date(userInfo.data.user.createdAt);
|
|
1153
|
+
}
|
|
1154
|
+
function generateYearRanges(accountCreated, now) {
|
|
1155
|
+
const years = [];
|
|
1156
|
+
for (let year = accountCreated.getFullYear(); year <= now.getFullYear(); year++) {
|
|
1157
|
+
const from = year === accountCreated.getFullYear() ? accountCreated.toISOString() : `${year}-01-01T00:00:00Z`;
|
|
1158
|
+
const to = year === now.getFullYear() ? now.toISOString() : `${year}-12-31T23:59:59Z`;
|
|
1159
|
+
years.push({ from, to });
|
|
1160
|
+
}
|
|
1161
|
+
return years;
|
|
1162
|
+
}
|
|
1163
|
+
async function fetchContributionsForYear(graphqlFetch, login, from, to) {
|
|
1164
|
+
const contributionsQuery = `
|
|
1165
|
+
query($login: String!, $from: DateTime!, $to: DateTime!) {
|
|
1166
|
+
user(login: $login) {
|
|
1167
|
+
contributionsCollection(from: $from, to: $to) {
|
|
1168
|
+
commitContributionsByRepository {
|
|
1169
|
+
repository {
|
|
1170
|
+
name
|
|
1171
|
+
nameWithOwner
|
|
1172
|
+
url
|
|
1173
|
+
owner { login url avatarUrl __typename }
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
`;
|
|
1180
|
+
const contributionsResp = await graphqlFetch({
|
|
1181
|
+
query: contributionsQuery,
|
|
1182
|
+
variables: { login, from, to }
|
|
1183
|
+
});
|
|
1184
|
+
return contributionsResp.data.user.contributionsCollection.commitContributionsByRepository.map((item) => item.repository).filter((repo) => repo?.nameWithOwner);
|
|
1185
|
+
}
|
|
1186
|
+
async function discoverReposFromContributions(graphqlFetch, login, repoMap) {
|
|
1187
|
+
console.log(`[contribkit][githubContributions] fetching contribution timeline to discover more repos...`);
|
|
1188
|
+
try {
|
|
1189
|
+
const accountCreated = await fetchUserCreationDate(graphqlFetch, login);
|
|
1190
|
+
const now = /* @__PURE__ */ new Date();
|
|
1191
|
+
const years = generateYearRanges(accountCreated, now);
|
|
1192
|
+
console.log(`[contribkit][githubContributions] querying contributions across ${years.length} years...`);
|
|
1193
|
+
for (const { from, to } of years) {
|
|
1194
|
+
try {
|
|
1195
|
+
const repos = await fetchContributionsForYear(graphqlFetch, login, from, to);
|
|
1196
|
+
for (const repo of repos) {
|
|
1197
|
+
repoMap.set(repo.nameWithOwner, repo);
|
|
1198
|
+
}
|
|
1199
|
+
} catch (e) {
|
|
1200
|
+
console.warn(`[contribkit][githubContributions] failed contributions query for ${from.slice(0, 4)}:`, e.message);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
} catch (e) {
|
|
1204
|
+
console.warn(`[contribkit][githubContributions] contribution timeline discovery failed:`, e.message);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
async function discoverReposFromMergedPRs(graphqlFetch, login, repoMap) {
|
|
1208
|
+
console.log(`[contribkit][githubContributions] searching for repos with merged PRs...`);
|
|
1209
|
+
try {
|
|
1210
|
+
const searchQueryBase = `is:pr is:merged author:${login}`;
|
|
1211
|
+
let searchAfter = null;
|
|
1212
|
+
let page = 0;
|
|
1213
|
+
const maxPages = 10;
|
|
1214
|
+
do {
|
|
1215
|
+
const response = await graphqlFetch({
|
|
1216
|
+
query: `
|
|
1217
|
+
query($searchQuery: String!, $after: String) {
|
|
1218
|
+
search(query: $searchQuery, type: ISSUE, first: 100, after: $after) {
|
|
1219
|
+
pageInfo { hasNextPage endCursor }
|
|
1220
|
+
edges { node { ... on PullRequest { repository { name nameWithOwner url owner { login url avatarUrl __typename } } } } }
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
`,
|
|
1224
|
+
variables: { searchQuery: searchQueryBase, after: searchAfter }
|
|
1225
|
+
});
|
|
1226
|
+
for (const edge of response.data.search.edges) {
|
|
1227
|
+
const r = edge.node.repository;
|
|
1228
|
+
if (r?.nameWithOwner)
|
|
1229
|
+
repoMap.set(r.nameWithOwner, r);
|
|
1230
|
+
}
|
|
1231
|
+
searchAfter = response.data.search.pageInfo.endCursor;
|
|
1232
|
+
page++;
|
|
1233
|
+
if (response.data.search.pageInfo.hasNextPage && page < maxPages)
|
|
1234
|
+
console.log(`[contribkit][githubContributions] merged PR search page ${page}, ${repoMap.size} repos so far`);
|
|
1235
|
+
} while (searchAfter && page < maxPages);
|
|
1236
|
+
} catch (e) {
|
|
1237
|
+
console.warn(`[contribkit][githubContributions] merged PR search failed:`, e.message);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
async function fetchPRCountForRepo(graphqlFetch, repo, login) {
|
|
1241
|
+
const searchQuery = `repo:${repo.nameWithOwner} is:pr is:merged author:${login}`;
|
|
1242
|
+
try {
|
|
1243
|
+
const response = await graphqlFetch({
|
|
1244
|
+
query: `query($q: String!) { search(query: $q, type: ISSUE) { issueCount } }`,
|
|
1245
|
+
variables: { q: searchQuery }
|
|
1246
|
+
});
|
|
1247
|
+
return response.data.search.issueCount;
|
|
1248
|
+
} catch (e) {
|
|
1249
|
+
console.warn(`[contribkit][githubContributions] failed PR count for ${repo.nameWithOwner}:`, e.message);
|
|
1250
|
+
return 0;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
async function fetchMergedPRCounts(graphqlFetch, allRepos, login) {
|
|
1254
|
+
console.log(`[contribkit][githubContributions] fetching merged PR counts per repository...`);
|
|
1255
|
+
const repoPRs = /* @__PURE__ */ new Map();
|
|
1256
|
+
const batchSize = 10;
|
|
1257
|
+
for (let i = 0; i < allRepos.length; i += batchSize) {
|
|
1258
|
+
const batch = allRepos.slice(i, i + batchSize);
|
|
1259
|
+
const counts = await Promise.all(batch.map((repo) => fetchPRCountForRepo(graphqlFetch, repo, login)));
|
|
1260
|
+
for (let index = 0; index < batch.length; index++) {
|
|
1261
|
+
const count = counts[index];
|
|
1262
|
+
if (count > 0)
|
|
1263
|
+
repoPRs.set(batch[index].nameWithOwner, count);
|
|
1264
|
+
}
|
|
1265
|
+
if (i + batchSize < allRepos.length)
|
|
1266
|
+
console.log(`[contribkit][githubContributions] processed PR batches for ${Math.min(i + batchSize, allRepos.length)}/${allRepos.length} repos...`);
|
|
1267
|
+
}
|
|
1268
|
+
console.log(`[contribkit][githubContributions] found merged PR counts for ${repoPRs.size} repositories`);
|
|
1269
|
+
return repoPRs;
|
|
1270
|
+
}
|
|
1271
|
+
function aggregateByOwner(results) {
|
|
1272
|
+
const aggregated = /* @__PURE__ */ new Map();
|
|
1273
|
+
for (const { repo, prs } of results) {
|
|
1274
|
+
const key = `${repo.owner.__typename}:${repo.owner.login}`;
|
|
1275
|
+
const existing = aggregated.get(key);
|
|
1276
|
+
if (existing) {
|
|
1277
|
+
existing.totalPRs += prs;
|
|
1278
|
+
existing.repos.push({ repo, prs });
|
|
1279
|
+
} else {
|
|
1280
|
+
aggregated.set(key, { owner: repo.owner, totalPRs: prs, repos: [{ repo, prs }] });
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
return aggregated;
|
|
1284
|
+
}
|
|
1285
|
+
function logConsolidatedOwners(aggregated) {
|
|
1286
|
+
const consolidated = Array.from(aggregated.values()).filter((a) => a.repos.length > 1);
|
|
1287
|
+
if (consolidated.length) {
|
|
1288
|
+
console.log(`[contribkit][githubContributions] consolidated ${consolidated.length} owners with multiple repos:`);
|
|
1289
|
+
for (const { owner, repos, totalPRs } of consolidated.toSorted((a, b) => b.repos.length - a.repos.length).slice(0, 10))
|
|
1290
|
+
console.log(` - ${owner.login}: ${repos.length} repos, ${totalPRs} merged PRs`);
|
|
1291
|
+
if (consolidated.length > 10)
|
|
1292
|
+
console.log(` ... and ${consolidated.length - 10} more`);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
function applyContributionScaling(totalPRs, maxContributions, logarithmicScaling) {
|
|
1296
|
+
let scaled = totalPRs;
|
|
1297
|
+
if (logarithmicScaling && scaled > 0) {
|
|
1298
|
+
scaled = Math.log10(scaled + 1) * 10;
|
|
1299
|
+
}
|
|
1300
|
+
if (maxContributions !== void 0 && scaled > maxContributions) {
|
|
1301
|
+
scaled = maxContributions;
|
|
1302
|
+
}
|
|
1303
|
+
return scaled;
|
|
1304
|
+
}
|
|
1305
|
+
function convertToSponsorships(aggregated, maxContributions, logarithmicScaling) {
|
|
1306
|
+
return Array.from(aggregated.values()).sort((a, b) => b.totalPRs - a.totalPRs).map(({ owner, totalPRs, repos }) => {
|
|
1307
|
+
const scaledPRs = applyContributionScaling(totalPRs, maxContributions, logarithmicScaling);
|
|
1308
|
+
const linkUrl = repos.length === 1 ? repos[0].repo.url : owner.url;
|
|
1309
|
+
return {
|
|
1310
|
+
sponsor: { type: owner.__typename, login: owner.login, name: owner.login, avatarUrl: owner.avatarUrl, linkUrl, socialLogins: { github: owner.login } },
|
|
1311
|
+
isOneTime: false,
|
|
1312
|
+
monthlyDollars: scaledPRs,
|
|
1313
|
+
privacyLevel: "PUBLIC",
|
|
1314
|
+
tierName: "Repository",
|
|
1315
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1316
|
+
provider: "githubContributions",
|
|
1317
|
+
raw: { owner, totalPRs, scaledPRs, repoCount: repos.length }
|
|
1318
|
+
};
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
async function fetchGitHubContributions(token, login, maxContributions, logarithmicScaling) {
|
|
1322
|
+
if (!token)
|
|
1323
|
+
throw new Error("GitHub token is required");
|
|
1324
|
+
if (!login)
|
|
1325
|
+
throw new Error("GitHub login is required");
|
|
1326
|
+
const graphqlFetch = createGraphQLFetch(token);
|
|
1327
|
+
console.log(`[contribkit][githubContributions] discovering repositories (sources: contributionsCollection + merged PR search)...`);
|
|
1328
|
+
const repoMap = /* @__PURE__ */ new Map();
|
|
1329
|
+
await discoverReposFromContributions(graphqlFetch, login, repoMap);
|
|
1330
|
+
console.log(`[contribkit][githubContributions] found ${repoMap.size} repos after contribution timeline`);
|
|
1331
|
+
await discoverReposFromMergedPRs(graphqlFetch, login, repoMap);
|
|
1332
|
+
console.log(`[contribkit][githubContributions] found ${repoMap.size} repos after merged PR search`);
|
|
1333
|
+
const allRepos = Array.from(repoMap.values());
|
|
1334
|
+
console.log(`[contribkit][githubContributions] discovered ${allRepos.length} total unique repositories`);
|
|
1335
|
+
const repoPRs = await fetchMergedPRCounts(graphqlFetch, allRepos, login);
|
|
1336
|
+
const results = [];
|
|
1337
|
+
for (const repo of allRepos) {
|
|
1338
|
+
const prs = repoPRs.get(repo.nameWithOwner) || 0;
|
|
1339
|
+
if (prs > 0)
|
|
1340
|
+
results.push({ repo, prs });
|
|
1341
|
+
}
|
|
1342
|
+
console.log(`[contribkit][githubContributions] computed merged PR counts for ${results.length} repositories (from ${allRepos.length} total repos with PRs)`);
|
|
1343
|
+
const aggregated = aggregateByOwner(results);
|
|
1344
|
+
logConsolidatedOwners(aggregated);
|
|
1345
|
+
const scalingInfo = [];
|
|
1346
|
+
if (maxContributions !== void 0)
|
|
1347
|
+
scalingInfo.push(`max cap: ${maxContributions}`);
|
|
1348
|
+
if (logarithmicScaling)
|
|
1349
|
+
scalingInfo.push("logarithmic scaling enabled");
|
|
1350
|
+
if (scalingInfo.length > 0)
|
|
1351
|
+
console.log(`[contribkit][githubContributions] applying contribution scaling: ${scalingInfo.join(", ")}`);
|
|
1352
|
+
return convertToSponsorships(aggregated, maxContributions, logarithmicScaling);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1108
1355
|
const GitlabContributorsProvider = {
|
|
1109
1356
|
name: "gitlabContributors",
|
|
1110
1357
|
fetchSponsors(config) {
|
|
@@ -1665,6 +1912,7 @@ const ProvidersMap = {
|
|
|
1665
1912
|
polar: PolarProvider,
|
|
1666
1913
|
liberapay: LiberapayProvider,
|
|
1667
1914
|
githubContributors: GitHubContributorsProvider,
|
|
1915
|
+
githubContributions: GitHubContributionsProvider,
|
|
1668
1916
|
gitlabContributors: GitlabContributorsProvider,
|
|
1669
1917
|
crowdinContributors: CrowdinContributorsProvider
|
|
1670
1918
|
};
|
|
@@ -1684,6 +1932,8 @@ function guessProviders(config) {
|
|
|
1684
1932
|
items.push("liberapay");
|
|
1685
1933
|
if (config.githubContributors?.login && config.githubContributors?.token)
|
|
1686
1934
|
items.push("githubContributors");
|
|
1935
|
+
if (config.githubContributions?.login && config.githubContributions?.token)
|
|
1936
|
+
items.push("githubContributions");
|
|
1687
1937
|
if (config.gitlabContributors?.token && config.gitlabContributors?.repoId)
|
|
1688
1938
|
items.push("gitlabContributors");
|
|
1689
1939
|
if (config.crowdinContributors?.token && config.crowdinContributors?.projectId)
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/LizardByte/contribkit.git"
|
|
7
7
|
},
|
|
8
|
-
"version": "2025.
|
|
8
|
+
"version": "2025.1130.1103",
|
|
9
9
|
"description": "Toolkit for generating contributor images",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"funding": "https://app.lizardbyte.dev",
|