@stats-forge/github-stats-forge-core 0.4.0 → 0.5.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/build/cards/organization/index.d.ts.map +1 -1
- package/build/cards/organization/index.js +2 -1
- package/build/common/http.d.ts +2 -0
- package/build/common/http.d.ts.map +1 -1
- package/build/common/languageColors.json +2 -0
- package/build/fetchers/organization.d.ts.map +1 -1
- package/build/fetchers/organization.js +17 -2
- package/build/fetchers/types.d.ts +2 -1
- package/build/fetchers/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cards/organization/index.ts +2 -1
- package/src/common/http.ts +6 -1
- package/src/common/languageColors.json +2 -0
- package/src/fetchers/organization.ts +21 -2
- package/src/fetchers/types.ts +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cards/organization/index.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AA6BpE,UAAU,cAAe,SAAQ,iBAAiB;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;CACpB;AAkCD;;;;GAIG;AACH,QAAA,MAAM,UAAU,iBACA,gBAAgB,YACrB,WAAW,CAAC,cAAc,CAAC,KACnC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cards/organization/index.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AA6BpE,UAAU,cAAe,SAAQ,iBAAiB;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;CACpB;AAkCD;;;;GAIG;AACH,QAAA,MAAM,UAAU,iBACA,gBAAgB,YACrB,WAAW,CAAC,cAAc,CAAC,KACnC,MAgPF,CAAC;AAEF;;;;GAIG;AACH,QAAA,MAAM,sBAAsB;;QAExB,IAAI;QACJ,IAAI;QACJ,aAAa;;CAEf,CAAC;AAEH,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
|
|
@@ -118,7 +118,8 @@ const renderCard = (organization, options = {}) => {
|
|
|
118
118
|
id: 'commits',
|
|
119
119
|
};
|
|
120
120
|
}
|
|
121
|
-
|
|
121
|
+
// A refused member count is `null`, and no row is better than a row reading "0".
|
|
122
|
+
if (shows('members') && publicMembers !== null) {
|
|
122
123
|
STATS['members'] = {
|
|
123
124
|
icon: icons.people,
|
|
124
125
|
label: t.members(),
|
package/build/common/http.d.ts
CHANGED
|
@@ -37,6 +37,8 @@ type GraphQLResponse<TResult> = HttpResponse<{
|
|
|
37
37
|
errors?: Array<{
|
|
38
38
|
type?: string;
|
|
39
39
|
message?: string;
|
|
40
|
+
/** The field the error is about, from the operation root: `['organization', 'membersWithRole']`. */
|
|
41
|
+
path?: Array<string | number>;
|
|
40
42
|
}>;
|
|
41
43
|
}>;
|
|
42
44
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/common/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAIrE;;;GAGG;AACH,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAE1E;;;GAGG;AACH,QAAA,MAAM,YAAY,EAAE,SAA+C,CAAC;AAEpE;;;;GAIG;AACH,UAAU,YAAY,CAAC,KAAK;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,KAAK,CAAC;CACb;AAED,wDAAwD;AACxD,UAAU,cAAc;IACtB,0CAA0C;IAC1C,KAAK,EAAE,SAAS,CAAC;IACjB,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,QAAA,MAAM,WAAW,GAAU,KAAK,aACnB,SAAS,OACf,MAAM,SACJ,WAAW,KACjB,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAiB7B,CAAC;AAEF,gFAAgF;AAChF,KAAK,eAAe,CAAC,OAAO,IAAI,YAAY,CAAC;IAC3C,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/common/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAIrE;;;GAGG;AACH,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAE1E;;;GAGG;AACH,QAAA,MAAM,YAAY,EAAE,SAA+C,CAAC;AAEpE;;;;GAIG;AACH,UAAU,YAAY,CAAC,KAAK;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,KAAK,CAAC;CACb;AAED,wDAAwD;AACxD,UAAU,cAAc;IACtB,0CAA0C;IAC1C,KAAK,EAAE,SAAS,CAAC;IACjB,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,QAAA,MAAM,WAAW,GAAU,KAAK,aACnB,SAAS,OACf,MAAM,SACJ,WAAW,KACjB,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAiB7B,CAAC;AAEF,gFAAgF;AAChF,KAAK,eAAe,CAAC,OAAO,IAAI,YAAY,CAAC;IAC3C,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,KAAK,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,oGAAoG;QACpG,IAAI,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;KAC/B,CAAC,CAAC;CACJ,CAAC,CAAC;AAEH;;GAEG;AACH,QAAA,MAAM,oBAAoB,GACvB,OAAO,EAAE,UAAU,YACR,eAAe,CAAC,OAAO,EAAE,UAAU,CAAC,UACtC,QAAQ,GAAG,OAAO,iBAGf,UAAU,SACd,MAAM,aACF,cAAc,KACxB,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAQ/B,CAAC;AAEP,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;AAC3D,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"Apollo Guidance Computer": "#0B3D91",
|
|
34
34
|
"AppleScript": "#101F1F",
|
|
35
35
|
"Arc": "#aa2afe",
|
|
36
|
+
"ArkTS": "#0080ff",
|
|
36
37
|
"AsciiDoc": "#73a0c5",
|
|
37
38
|
"AspectJ": "#a957b0",
|
|
38
39
|
"Assembly": "#6E4C13",
|
|
@@ -322,6 +323,7 @@
|
|
|
322
323
|
"Kotlin": "#A97BFF",
|
|
323
324
|
"LFE": "#4C3023",
|
|
324
325
|
"LLVM": "#185619",
|
|
326
|
+
"LLVM TableGen": "#6E8B3D",
|
|
325
327
|
"LOLCODE": "#cc9900",
|
|
326
328
|
"LSL": "#3d9970",
|
|
327
329
|
"LabVIEW": "#fede06",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"organization.d.ts","sourceRoot":"","sources":["../../src/fetchers/organization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"organization.d.ts","sourceRoot":"","sources":["../../src/fetchers/organization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AActD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA+DnD;;;;;;;GAOG;AACH,QAAA,MAAM,iBAAiB,YACZ;IAAE,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,UAC5B,UAAU,KACjB,OAAO,CAAC,gBAAgB,CAiF1B,CAAC;AAEF,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { GITHUB_USERNAME_PATTERN } from '../common/constants.js';
|
|
2
2
|
import { CardError, ORGANIZATION_NOT_FOUND } from '../common/error.js';
|
|
3
3
|
import { createGraphQLFetcher } from '../common/http.js';
|
|
4
|
+
import { logger } from '../common/log.js';
|
|
4
5
|
import { retryer } from '../common/retryer.js';
|
|
5
6
|
import { GetOrganizationDocument } from '../graphql/generated/organization.js';
|
|
6
7
|
import { graphqlError } from './graphql-error.js';
|
|
@@ -38,6 +39,15 @@ const topLanguage = (repos) => {
|
|
|
38
39
|
}
|
|
39
40
|
return top;
|
|
40
41
|
};
|
|
42
|
+
/**
|
|
43
|
+
* The member count is the one field here a token can be refused:
|
|
44
|
+
* it needs the organization `Members` permission, which an app installation token rarely carries.
|
|
45
|
+
* GitHub answers `FORBIDDEN` on that field alone and still returns the organization,
|
|
46
|
+
* so the card drops the stat instead of failing whole.
|
|
47
|
+
*
|
|
48
|
+
* @returns Whether the member count is all the response was refused.
|
|
49
|
+
*/
|
|
50
|
+
const onlyMembersForbidden = (errors) => errors.every((error) => error.type === 'FORBIDDEN' && error.path?.at(-1) === 'membersWithRole');
|
|
41
51
|
/**
|
|
42
52
|
* Fetch an organization and the totals of its public repositories.
|
|
43
53
|
*
|
|
@@ -61,6 +71,7 @@ const fetchOrganization = async ({ org }, config) => {
|
|
|
61
71
|
let organization;
|
|
62
72
|
let after = null;
|
|
63
73
|
let pages = 0;
|
|
74
|
+
let membersForbidden = false;
|
|
64
75
|
do {
|
|
65
76
|
// Annotated because the walk feeds its own cursor back in, which TypeScript reads as circular.
|
|
66
77
|
const res = await retryer(fetcher, { login: org, after }, config);
|
|
@@ -73,7 +84,11 @@ const fetchOrganization = async ({ org }, config) => {
|
|
|
73
84
|
secondaryMessage: ORGANIZATION_NOT_FOUND,
|
|
74
85
|
});
|
|
75
86
|
}
|
|
76
|
-
|
|
87
|
+
if (!onlyMembersForbidden(res.data.errors)) {
|
|
88
|
+
throw graphqlError(res.data.errors, res.statusText, ORGANIZATION_ERROR);
|
|
89
|
+
}
|
|
90
|
+
logger.log(res.data.errors[0]?.message);
|
|
91
|
+
membersForbidden = true;
|
|
77
92
|
}
|
|
78
93
|
organization = res.data.data.organization ?? undefined;
|
|
79
94
|
if (!organization) {
|
|
@@ -107,7 +122,7 @@ const fetchOrganization = async ({ org }, config) => {
|
|
|
107
122
|
? target.history.totalCount
|
|
108
123
|
: 0;
|
|
109
124
|
}),
|
|
110
|
-
publicMembers: organization.membersWithRole.totalCount,
|
|
125
|
+
publicMembers: membersForbidden ? null : organization.membersWithRole.totalCount,
|
|
111
126
|
topLanguage: topLanguage(repos),
|
|
112
127
|
truncated: after !== null,
|
|
113
128
|
};
|
|
@@ -55,8 +55,9 @@ export interface OrganizationData {
|
|
|
55
55
|
/**
|
|
56
56
|
* Members GitHub shows publicly.
|
|
57
57
|
* Membership is private by default, so this counts fewer people than the organization has.
|
|
58
|
+
* `null` when the token may not read them, which is the card's cue to leave the stat out.
|
|
58
59
|
*/
|
|
59
|
-
publicMembers: number;
|
|
60
|
+
publicMembers: number | null;
|
|
60
61
|
/** The language most of those repositories name as their primary one, `null` when none do. */
|
|
61
62
|
topLanguage: {
|
|
62
63
|
name: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/fetchers/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,KAAK,QAAQ,GAAG,IAAI,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,GAAG;IAC1D,eAAe,EAAE;QACf,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;KACrB,GAAG,IAAI,CAAC;CACV,CAAC;AAEF,MAAM,WAAW,aAAa;IAE5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,aAAa,CAAC;AAEtD;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB,4FAA4F;IAC5F,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/fetchers/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -211,7 +211,8 @@ const renderCard = (
|
|
|
211
211
|
};
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
|
|
214
|
+
// A refused member count is `null`, and no row is better than a row reading "0".
|
|
215
|
+
if (shows('members') && publicMembers !== null) {
|
|
215
216
|
STATS['members'] = {
|
|
216
217
|
icon: icons.people,
|
|
217
218
|
label: t.members(),
|
package/src/common/http.ts
CHANGED
|
@@ -63,7 +63,12 @@ const httpRequest = async <TData>(
|
|
|
63
63
|
/** Response of a GraphQL call: the envelope the GitHub API wraps results in. */
|
|
64
64
|
type GraphQLResponse<TResult> = HttpResponse<{
|
|
65
65
|
data: TResult;
|
|
66
|
-
errors?: Array<{
|
|
66
|
+
errors?: Array<{
|
|
67
|
+
type?: string;
|
|
68
|
+
message?: string;
|
|
69
|
+
/** The field the error is about, from the operation root: `['organization', 'membersWithRole']`. */
|
|
70
|
+
path?: Array<string | number>;
|
|
71
|
+
}>;
|
|
67
72
|
}>;
|
|
68
73
|
|
|
69
74
|
/**
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"Apollo Guidance Computer": "#0B3D91",
|
|
34
34
|
"AppleScript": "#101F1F",
|
|
35
35
|
"Arc": "#aa2afe",
|
|
36
|
+
"ArkTS": "#0080ff",
|
|
36
37
|
"AsciiDoc": "#73a0c5",
|
|
37
38
|
"AspectJ": "#a957b0",
|
|
38
39
|
"Assembly": "#6E4C13",
|
|
@@ -322,6 +323,7 @@
|
|
|
322
323
|
"Kotlin": "#A97BFF",
|
|
323
324
|
"LFE": "#4C3023",
|
|
324
325
|
"LLVM": "#185619",
|
|
326
|
+
"LLVM TableGen": "#6E8B3D",
|
|
325
327
|
"LOLCODE": "#cc9900",
|
|
326
328
|
"LSL": "#3d9970",
|
|
327
329
|
"LabVIEW": "#fede06",
|
|
@@ -3,6 +3,7 @@ import { GITHUB_USERNAME_PATTERN } from '../common/constants.ts';
|
|
|
3
3
|
import { CardError, ORGANIZATION_NOT_FOUND } from '../common/error.ts';
|
|
4
4
|
import { createGraphQLFetcher } from '../common/http.ts';
|
|
5
5
|
import type { GraphQLResponse } from '../common/http.ts';
|
|
6
|
+
import { logger } from '../common/log.ts';
|
|
6
7
|
import { retryer } from '../common/retryer.ts';
|
|
7
8
|
import { GetOrganizationDocument } from '../graphql/generated/organization.ts';
|
|
8
9
|
import type {
|
|
@@ -61,6 +62,19 @@ const topLanguage = (
|
|
|
61
62
|
return top;
|
|
62
63
|
};
|
|
63
64
|
|
|
65
|
+
/**
|
|
66
|
+
* The member count is the one field here a token can be refused:
|
|
67
|
+
* it needs the organization `Members` permission, which an app installation token rarely carries.
|
|
68
|
+
* GitHub answers `FORBIDDEN` on that field alone and still returns the organization,
|
|
69
|
+
* so the card drops the stat instead of failing whole.
|
|
70
|
+
*
|
|
71
|
+
* @returns Whether the member count is all the response was refused.
|
|
72
|
+
*/
|
|
73
|
+
const onlyMembersForbidden = (
|
|
74
|
+
errors: NonNullable<GraphQLResponse<unknown>['data']['errors']>,
|
|
75
|
+
): boolean =>
|
|
76
|
+
errors.every((error) => error.type === 'FORBIDDEN' && error.path?.at(-1) === 'membersWithRole');
|
|
77
|
+
|
|
64
78
|
/**
|
|
65
79
|
* Fetch an organization and the totals of its public repositories.
|
|
66
80
|
*
|
|
@@ -88,6 +102,7 @@ const fetchOrganization = async (
|
|
|
88
102
|
let organization: Organization | undefined;
|
|
89
103
|
let after: string | null = null;
|
|
90
104
|
let pages = 0;
|
|
105
|
+
let membersForbidden = false;
|
|
91
106
|
|
|
92
107
|
do {
|
|
93
108
|
// Annotated because the walk feeds its own cursor back in, which TypeScript reads as circular.
|
|
@@ -105,7 +120,11 @@ const fetchOrganization = async (
|
|
|
105
120
|
secondaryMessage: ORGANIZATION_NOT_FOUND,
|
|
106
121
|
});
|
|
107
122
|
}
|
|
108
|
-
|
|
123
|
+
if (!onlyMembersForbidden(res.data.errors)) {
|
|
124
|
+
throw graphqlError(res.data.errors, res.statusText, ORGANIZATION_ERROR);
|
|
125
|
+
}
|
|
126
|
+
logger.log(res.data.errors[0]?.message);
|
|
127
|
+
membersForbidden = true;
|
|
109
128
|
}
|
|
110
129
|
|
|
111
130
|
organization = res.data.data.organization ?? undefined;
|
|
@@ -144,7 +163,7 @@ const fetchOrganization = async (
|
|
|
144
163
|
? target.history.totalCount
|
|
145
164
|
: 0;
|
|
146
165
|
}),
|
|
147
|
-
publicMembers: organization.membersWithRole.totalCount,
|
|
166
|
+
publicMembers: membersForbidden ? null : organization.membersWithRole.totalCount,
|
|
148
167
|
topLanguage: topLanguage(repos),
|
|
149
168
|
truncated: after !== null,
|
|
150
169
|
};
|
package/src/fetchers/types.ts
CHANGED
|
@@ -61,8 +61,9 @@ export interface OrganizationData {
|
|
|
61
61
|
/**
|
|
62
62
|
* Members GitHub shows publicly.
|
|
63
63
|
* Membership is private by default, so this counts fewer people than the organization has.
|
|
64
|
+
* `null` when the token may not read them, which is the card's cue to leave the stat out.
|
|
64
65
|
*/
|
|
65
|
-
publicMembers: number;
|
|
66
|
+
publicMembers: number | null;
|
|
66
67
|
/** The language most of those repositories name as their primary one, `null` when none do. */
|
|
67
68
|
topLanguage: { name: string; color: string | null } | null;
|
|
68
69
|
/** Whether the repository walk stopped at its page cap, leaving the totals short. */
|