github-portfolio-analyzer 1.4.1 → 1.4.3
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 +17 -0
- package/analyzer.manifest.json +1 -1
- package/package.json +1 -1
- package/schemas/portfolio-report.schema.json +6 -1
- package/src/core/report.js +4 -1
- package/src/github/repos.js +29 -4
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.4.3] - 2026-04-04
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- `classifyFork`: removed unused `asOfDate` parameter from signature and all call sites.
|
|
12
|
+
The parameter was added for a date-based heuristic removed in v1.4.1 and was never
|
|
13
|
+
read inside the function body.
|
|
14
|
+
|
|
15
|
+
## [1.4.2] - 2026-04-03
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- `normalizeRepository` now explicitly coerces `private` to boolean, preventing `undefined` from propagating through the pipeline and causing private repos to be treated as public.
|
|
19
|
+
- `buildReportModel` always includes `private` in output (was omitted when falsy), ensuring the worker always receives an explicit value.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- Language breakdown via GitHub `/languages` API endpoint. Each repo now has a `languages` field (`Record<string, number>`) with byte counts per language, enabling multi-language badge display in the portfolio frontend.
|
|
23
|
+
|
|
7
24
|
## [1.4.1] - 2026-04-03
|
|
8
25
|
|
|
9
26
|
### Fixed
|
package/analyzer.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -203,7 +203,8 @@
|
|
|
203
203
|
"priorityBand",
|
|
204
204
|
"priorityOverrides",
|
|
205
205
|
"priorityWhy",
|
|
206
|
-
"nextAction"
|
|
206
|
+
"nextAction",
|
|
207
|
+
"private"
|
|
207
208
|
],
|
|
208
209
|
"properties": {
|
|
209
210
|
"slug": { "type": "string" },
|
|
@@ -243,6 +244,10 @@
|
|
|
243
244
|
},
|
|
244
245
|
"nextAction": { "type": "string" },
|
|
245
246
|
"language": { "type": "string" },
|
|
247
|
+
"languages": {
|
|
248
|
+
"type": "object",
|
|
249
|
+
"additionalProperties": { "type": "number" }
|
|
250
|
+
},
|
|
246
251
|
"topics": {
|
|
247
252
|
"type": "array",
|
|
248
253
|
"items": { "type": "string" }
|
package/src/core/report.js
CHANGED
|
@@ -205,13 +205,16 @@ export function buildReportModel(portfolioData, inventoryData = null, options =
|
|
|
205
205
|
nextAction: String(item.nextAction ?? '').trim(),
|
|
206
206
|
// presentation fields — passed directly from portfolio item
|
|
207
207
|
...(item.language != null ? { language: item.language } : {}),
|
|
208
|
+
...(typeof item.languages === 'object' && item.languages !== null && !Array.isArray(item.languages) && Object.keys(item.languages).length > 0
|
|
209
|
+
? { languages: item.languages }
|
|
210
|
+
: {}),
|
|
208
211
|
...(Array.isArray(item.topics) && item.topics.length > 0 ? { topics: item.topics } : {}),
|
|
209
212
|
...(!isPrivate && item.htmlUrl != null ? { htmlUrl: item.htmlUrl } : {}),
|
|
210
213
|
...(!isPrivate && item.homepage != null ? { homepage: item.homepage } : {}),
|
|
211
214
|
...(item.category != null ? { category: item.category } : {}),
|
|
212
215
|
...(item.fork != null ? { fork: Boolean(item.fork) } : {}),
|
|
213
216
|
...(item.forkType != null ? { forkType: item.forkType } : {}),
|
|
214
|
-
|
|
217
|
+
private: Boolean(item.private ?? false),
|
|
215
218
|
...(item.publicAlias != null ? { publicAlias: item.publicAlias } : {}),
|
|
216
219
|
...(!isPrivate && item.description != null ? { description: item.description } : {}),
|
|
217
220
|
...(isPrivate && item.description != null ? { _description: item.description } : {})
|
package/src/github/repos.js
CHANGED
|
@@ -4,7 +4,7 @@ const PAGE_SIZE = 100;
|
|
|
4
4
|
* Classifies a fork as active or passive.
|
|
5
5
|
* Active forks are ahead of the upstream default branch.
|
|
6
6
|
*/
|
|
7
|
-
export async function classifyFork(client, repo
|
|
7
|
+
export async function classifyFork(client, repo) {
|
|
8
8
|
if (!repo?.fork) {
|
|
9
9
|
return null;
|
|
10
10
|
}
|
|
@@ -68,7 +68,28 @@ export async function fetchAllRepositories(client, asOfDate = new Date().toISOSt
|
|
|
68
68
|
const batch = forks.slice(index, index + 5);
|
|
69
69
|
await Promise.all(
|
|
70
70
|
batch.map(async (repository) => {
|
|
71
|
-
repository.forkType = await classifyFork(client, repository
|
|
71
|
+
repository.forkType = await classifyFork(client, repository);
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for (let index = 0; index < repositories.length; index += 5) {
|
|
77
|
+
const batch = repositories.slice(index, index + 5);
|
|
78
|
+
await Promise.all(
|
|
79
|
+
batch.map(async (repository) => {
|
|
80
|
+
const ownerLogin = repository.owner?.login ?? repository.ownerLogin ?? '';
|
|
81
|
+
if (!ownerLogin || !repository.name) {
|
|
82
|
+
repository.languages = {};
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
repository.languages = await client.request(
|
|
88
|
+
`/repos/${encodeURIComponent(ownerLogin)}/${encodeURIComponent(repository.name)}/languages`
|
|
89
|
+
);
|
|
90
|
+
} catch {
|
|
91
|
+
repository.languages = {};
|
|
92
|
+
}
|
|
72
93
|
})
|
|
73
94
|
);
|
|
74
95
|
}
|
|
@@ -81,9 +102,9 @@ export function normalizeRepository(repo) {
|
|
|
81
102
|
id: repo.id,
|
|
82
103
|
nodeId: repo.node_id,
|
|
83
104
|
name: repo.name,
|
|
84
|
-
ownerLogin: repo.owner?.login ?? '',
|
|
105
|
+
ownerLogin: repo.owner?.login ?? repo.ownerLogin ?? '',
|
|
85
106
|
fullName: repo.full_name,
|
|
86
|
-
private: repo.private,
|
|
107
|
+
private: Boolean(repo.private),
|
|
87
108
|
archived: repo.archived,
|
|
88
109
|
fork: repo.fork,
|
|
89
110
|
forkType: repo.forkType ?? null,
|
|
@@ -91,6 +112,10 @@ export function normalizeRepository(repo) {
|
|
|
91
112
|
htmlUrl: repo.html_url,
|
|
92
113
|
description: repo.description,
|
|
93
114
|
language: repo.language,
|
|
115
|
+
languages:
|
|
116
|
+
typeof repo.languages === 'object' && repo.languages !== null && !Array.isArray(repo.languages)
|
|
117
|
+
? repo.languages
|
|
118
|
+
: {},
|
|
94
119
|
homepage: typeof repo.homepage === 'string' && repo.homepage.trim().length > 0
|
|
95
120
|
? repo.homepage.trim()
|
|
96
121
|
: null,
|