github-lang-stats 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-client.js","sourceRoot":"","sources":["../src/github-client.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,GAAG,wBAAwB,CAAC;AACzC,MAAM,UAAU,GAAG,gCAAgC,CAAC;AAEpD,MAAM,OAAO,YAAY;IAChB,KAAK,CAAS;IACd,kBAAkB,GAAG,IAAI,CAAC;IAC1B,cAAc,GAAG,CAAC,CAAC;IAE3B,YAAY,KAAa;QACxB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;IAEO,OAAO;QACd,OAAO;YACN,aAAa,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE;YACrC,cAAc,EAAE,kBAAkB;YAClC,YAAY,EAAE,2BAA2B;YACzC,MAAM,EAAE,6BAA6B;YACrC,sBAAsB,EAAE,YAAY;SACpC,CAAC;IACH,CAAC;IAEO,0BAA0B,CAAC,OAAgB;QAClD,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QACvD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QAC/C,IAAI,SAAS,KAAK,IAAI;YAAE,IAAI,CAAC,kBAAkB,GAAG,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC1E,IAAI,KAAK,KAAK,IAAI;YAAE,IAAI,CAAC,cAAc,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,6CAA6C;IACrC,KAAK,CAAC,QAAQ;QACrB,IAAI,IAAI,CAAC,kBAAkB,IAAI,EAAE,EAAE,CAAC;YACnC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;YAC1C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,GAAG,GAAG,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;YAC/D,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,0CAA0C,WAAW,kBAAkB,CACvE,CAAC;YACF,MAAM,KAAK,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;QACjC,CAAC;IACF,CAAC;IAED,gBAAgB;QACf,OAAO;YACN,KAAK,EAAE,IAAI;YACX,SAAS,EAAE,IAAI,CAAC,kBAAkB;YAClC,KAAK,EAAE,IAAI,CAAC,cAAc;SAC1B,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,UAAU;IACV,8EAA8E;IAE9E,KAAK,CAAC,OAAO,CACZ,KAAa,EACb,SAAmC;QAEnC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,UAAU,EAAE;YACnC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;YACvB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;SAC1C,CAAC,CAAC;QACH,IAAI,CAAC,0BAA0B,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACb,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAC;QACpE,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAqC,CAAC;QACpE,IAAI,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;YACzB,sFAAsF;YACtF,IAAI,CAAC,IAAI,CAAC,IAAI;gBACb,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,IAAI,CAAC,IAAS,CAAC;IACvB,CAAC;IAED,8EAA8E;IAC9E,wFAAwF;IACxF,wFAAwF;IACxF,8DAA8D;IAC9D,8EAA8E;IAE9E,KAAK,CAAC,oBAAoB;QACzB,MAAM,KAAK,GAAW,EAAE,CAAC;QACzB,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,CAAC;QAEpB,OAAO,IAAI,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,MAAM,KAAK,CACtB,GAAG,OAAO,2EAA2E,OAAO,SAAS,IAAI,EAAE,EAC3G,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,CAC3B,CAAC;YACF,IAAI,CAAC,0BAA0B,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC7C,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;YAEtE,MAAM,QAAQ,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAIhC,CAAC;YACH,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM;YAEjC,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;gBAC1B,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACpD,CAAC;YAED,IAAI,QAAQ,CAAC,MAAM,GAAG,OAAO;gBAAE,MAAM;YACrC,IAAI,EAAE,CAAC;QACR,CAAC;QAED,OAAO,KAAK,CAAC;IACd,CAAC;IAED,8EAA8E;IAC9E,qEAAqE;IACrE,oFAAoF;IACpF,8EAA8E;IAE9E,KAAK,CAAC,6BAA6B,CAClC,IAAY,EACZ,QAAQ,GAAG,IAAI,EACf,UAAmC;QAEnC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAgB,CAAC;QACxC,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAE7C,KAAK,IAAI,IAAI,GAAG,QAAQ,EAAE,IAAI,IAAI,WAAW,EAAE,IAAI,EAAE,EAAE,CAAC;YACvD,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC;YACnB,MAAM,IAAI,GAAG,GAAG,IAAI,kBAAkB,CAAC;YACvC,MAAM,EAAE,GAAG,GAAG,IAAI,kBAAkB,CAAC;YAErC,IAAI,CAAC;gBACJ,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAS9B;;;;;;;;;;;;;SAaI,EACJ,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CACzB,CAAC;gBAEF,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,uBAAuB;qBACnD,+BAA+B,EAAE,CAAC;oBACnC,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;oBACvE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;wBACvB,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE;4BAChB,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK;4BACnC,IAAI,EAAE,KAAK,CAAC,UAAU,CAAC,IAAI;yBAC3B,CAAC,CAAC;oBACJ,CAAC;gBACF,CAAC;YACF,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,gDAAgD,IAAI,KAAK,MAAM,CAAC,GAAG,CAAC,IAAI,CACxE,CAAC;YACH,CAAC;YAED,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;QAED,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED,8EAA8E;IAC9E,kCAAkC;IAClC,8EAA8E;IAE9E,KAAK,CAAC,wBAAwB,CAC7B,IAAY,EACZ,QAAQ,GAAG,IAAI,EACf,UAAmC;QAEnC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAgB,CAAC;QAExC,oFAAoF;QACpF,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,oBAAoB,EAAE,CAAC;QACpD,KAAK,MAAM,CAAC,IAAI,SAAS;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;QAElE,gFAAgF;QAChF,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,6BAA6B,CAC5D,IAAI,EACJ,QAAQ,EACR,UAAU,CACV,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,YAAY;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;QAErE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED,8EAA8E;IAC9E,0DAA0D;IAC1D,8EAA8E;IAE9E,KAAK,CAAC,aAAa,CAAC,QAAgB;QACnC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAC9B,uDAAuD,EACvD,EAAE,KAAK,EAAE,QAAQ,EAAE,CACnB,CAAC;QACF,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IACrB,CAAC;IAED,8EAA8E;IAC9E,0EAA0E;IAC1E,8EAA8E;IAE9E,KAAK,CAAC,iBAAiB,CACtB,KAAa,EACb,IAAY,EACZ,QAAgB,EAChB,MAAgC,EAChC,QAAiB;QAajB,MAAM,IAAI,GAAa,EAAE,CAAC;QAC1B,IAAI,MAAM,GAAkB,IAAI,CAAC;QACjC,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,kBAAkB,CAAC,CAAC,CAAC,SAAS,CAAC;QAEnE,OAAO,IAAI,EAAE,CAAC;YACb,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;YACtB,IAAI,CAAC;gBACJ,MAAM,IAAI,GAAkB,MAAM,IAAI,CAAC,OAAO,CAC7C;;;;;;;;;;;;;;;SAeI,EACJ,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAC9C,CAAC;gBAEF,MAAM,OAAO,GACZ,IAAI,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,CAAC;gBACrD,IAAI,CAAC,OAAO;oBAAE,MAAM;gBAEpB,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;oBAClC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACrB,CAAC;gBAED,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAEtB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW;oBAAE,MAAM;gBACzC,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;YACrC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,yCAAyC,KAAK,IAAI,IAAI,KAAK,MAAM,CAAC,GAAG,CAAC,IAAI,CAC1E,CAAC;gBACF,MAAM;YACP,CAAC;QACF,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;IAED,8EAA8E;IAC9E,+CAA+C;IAC/C,8EAA8E;IAE9E,KAAK,CAAC,iBAAiB,CACtB,KAAa,EACb,IAAY,EACZ,GAAW;QAEX,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,GAAG,OAAO,UAAU,KAAK,IAAI,IAAI,YAAY,GAAG,EAAE,CAAC;QAC/D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,0BAA0B,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAE7C,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAC1D,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACb,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC9C,6CAA6C;gBAC7C,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;gBACxE,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,uCAAuC,UAAU,QAAQ,CACzD,CAAC;gBACF,MAAM,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;gBAC/B,OAAO,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YACjD,CAAC;YACD,MAAM,IAAI,KAAK,CACd,6BAA6B,GAAG,CAAC,MAAM,SAAS,KAAK,IAAI,IAAI,IAAI,GAAG,EAAE,CACtE,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAQ7B,CAAC;QAEF,OAAO;YACN,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,KAAK,EAAE,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACrC,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,MAAM,EAAE,CAAC,CAAC,MAAM;aAChB,CAAC,CAAC;SACH,CAAC;IACH,CAAC;CACD;AAED,SAAS,KAAK,CAAC,EAAU;IACxB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
+ import { dirname, resolve } from "node:path";
4
+ import checkbox from "@inquirer/checkbox";
5
+ import chalk from "chalk";
6
+ import { Command } from "commander";
7
+ import ora from "ora";
8
+ import { aggregate } from "./aggregator.js";
9
+ import { CacheStore, defaultCachePath } from "./cache.js";
10
+ import { GitHubClient } from "./github-client.js";
11
+ const DEFAULT_FROM_YEAR = new Date().getFullYear() - 10;
12
+ const program = new Command();
13
+ program
14
+ .name("gls")
15
+ .alias("github-lang-stats")
16
+ .description("Compute per-author GitHub language statistics by analysing commit file changes.\n" +
17
+ "Fetches only commits authored by --user, then inspects changed files via REST.\n" +
18
+ "Progress is cached locally so interrupted runs can be resumed.")
19
+ .version("1.0.0")
20
+ .requiredOption("-u, --user <username>", "GitHub username")
21
+ .requiredOption("-t, --token <pat>", "GitHub Personal Access Token (needs repo + read:user scopes)")
22
+ .option("-o, --output <path>", "Path to write JSON output (default: stdout)")
23
+ .option("--cache <path>", "Override cache file path")
24
+ .option("--no-cache", "Disable cache (start fresh each run)")
25
+ .option("--concurrency <n>", "Concurrent REST requests for commit details", "5")
26
+ .option("--from-year <year>", `Earliest year to include (default: ${DEFAULT_FROM_YEAR}, i.e. 10 years ago)`, String(DEFAULT_FROM_YEAR))
27
+ .option("--exclude-langs <langs>", "Comma-separated language names to exclude (e.g. JSON,YAML)")
28
+ .option("--select-repos", "Interactively select which repositories to analyse after fetching commit counts")
29
+ .option("--stats-only", "Skip fetching new data, just aggregate from existing cache")
30
+ .option("--reset", "Delete cache and start fresh")
31
+ .parse(process.argv);
32
+ const opts = program.opts();
33
+ const concurrency = parseInt(opts.concurrency, 10);
34
+ const fromYear = parseInt(opts.fromYear, 10);
35
+ const excludeLanguages = opts.excludeLangs
36
+ ? opts.excludeLangs.split(",").map((s) => s.trim())
37
+ : [];
38
+ // ─── Setup ────────────────────────────────────────────────────────────────────
39
+ const client = new GitHubClient(opts.token);
40
+ const cachePath = opts.cache ?? defaultCachePath(opts.user);
41
+ const cache = new CacheStore(cachePath);
42
+ if (opts.reset) {
43
+ console.log(chalk.yellow(`Resetting cache at ${cachePath}`));
44
+ cache.reset();
45
+ cache.save();
46
+ }
47
+ console.log(chalk.bold(`\ngithub-lang-stats`) +
48
+ chalk.gray(` — user: ${opts.user}`) +
49
+ chalk.gray(` — from ${fromYear}`));
50
+ if (!opts.noCache)
51
+ console.log(chalk.gray(`Cache: ${cachePath}`));
52
+ console.log();
53
+ // ─── Phase 1: Discover repos ──────────────────────────────────────────────────
54
+ if (!opts.statsOnly) {
55
+ if (cache.repos.length === 0) {
56
+ const spinner = ora("Discovering contributed repositories (year-by-year)…").start();
57
+ let currentYear = 0;
58
+ const repos = await client.discoverContributedRepos(opts.user, fromYear, (year) => {
59
+ currentYear = year;
60
+ spinner.text = `Scanning contributions for ${year}…`;
61
+ });
62
+ cache.repos = repos;
63
+ if (!opts.noCache)
64
+ cache.save();
65
+ spinner.succeed(`Found ${chalk.bold(repos.length)} repositories (${fromYear}–${currentYear} scanned)`);
66
+ }
67
+ else {
68
+ console.log(chalk.green(`✓`) +
69
+ ` Using cached repo list: ${chalk.bold(cache.repos.length)} repos`);
70
+ }
71
+ // ─── Phase 2: Collect commit SHAs per repo ──────────────────────────────────
72
+ const incompleteRepos = cache.repos.filter((r) => !cache.isRepoComplete(r.owner, r.name));
73
+ if (incompleteRepos.length > 0) {
74
+ console.log(`\nCollecting commit SHAs for ${chalk.bold(incompleteRepos.length)} repos…`);
75
+ // Use the user's GitHub node ID for reliable author filtering
76
+ // (email-based filtering misses commits made with different local git emails)
77
+ const spinner0 = ora("Fetching GitHub user node ID…").start();
78
+ const authorId = await client.getUserNodeId(opts.user);
79
+ spinner0.succeed(`Author ID: ${chalk.gray(authorId)}`);
80
+ let repoIdx = 0;
81
+ for (const repo of incompleteRepos) {
82
+ repoIdx++;
83
+ const spinner = ora(`[${repoIdx}/${incompleteRepos.length}] ${repo.owner}/${repo.name}…`).start();
84
+ const existing = cache.getCommits(repo.owner, repo.name);
85
+ const shas = await client.collectCommitShas(repo.owner, repo.name, authorId, (count) => {
86
+ spinner.text = `[${repoIdx}/${incompleteRepos.length}] ${repo.owner}/${repo.name} — ${count} commits…`;
87
+ }, fromYear);
88
+ cache.addCommits(repo.owner, repo.name, shas);
89
+ cache.markRepoComplete(repo.owner, repo.name);
90
+ if (!opts.noCache)
91
+ cache.save();
92
+ const fresh = shas.length - existing.length;
93
+ spinner.succeed(`[${repoIdx}/${incompleteRepos.length}] ${repo.owner}/${repo.name} — ` +
94
+ `${chalk.bold(shas.length)} commits` +
95
+ (fresh > 0 ? chalk.gray(` (+${fresh} new)`) : ""));
96
+ }
97
+ }
98
+ else {
99
+ console.log(`${chalk.green(`✓`)} All repo commit SHAs already cached`);
100
+ }
101
+ // ─── Phase 2.5: Interactive repo selection ────────────────────────────────────
102
+ if (opts.selectRepos) {
103
+ const repoEntries = cache.repos
104
+ .map((r) => ({
105
+ repo: r,
106
+ key: `${r.owner}/${r.name}`,
107
+ count: cache.getCommits(r.owner, r.name).length
108
+ }))
109
+ .sort((a, b) => b.count - a.count);
110
+ console.log(`\n${chalk.bold("Select repositories to analyse")} ` +
111
+ chalk.gray("(space=toggle, a=toggle all, i=invert, enter=confirm)\n"));
112
+ const selected = await checkbox({
113
+ message: `Choose repos (${repoEntries.length} total, sorted by commit count)`,
114
+ choices: repoEntries.map(({ key, count }) => ({
115
+ name: `${key.padEnd(55)} ${chalk.gray(`${String(count).padStart(5)} commits`)}`,
116
+ value: key,
117
+ checked: true
118
+ })),
119
+ pageSize: 20,
120
+ loop: false
121
+ });
122
+ if (selected.length === 0) {
123
+ console.log(chalk.red("No repositories selected — nothing to do."));
124
+ process.exit(0);
125
+ }
126
+ const selectedSet = new Set(selected);
127
+ cache.repos = cache.repos.filter((r) => selectedSet.has(`${r.owner}/${r.name}`));
128
+ console.log(chalk.green(`✓`) +
129
+ ` Analysing ${chalk.bold(cache.repos.length)} selected repos\n`);
130
+ }
131
+ // ─── Phase 3: Fetch commit file details (REST) ──────────────────────────────
132
+ const pending = cache.pendingCommitCount();
133
+ const total = cache.totalCommitShas();
134
+ const done = total - pending;
135
+ if (pending > 0) {
136
+ console.log(`\nFetching commit details: ${chalk.bold(pending)} remaining` +
137
+ (done > 0 ? chalk.gray(` (${done} already cached)`) : "") +
138
+ `\n` +
139
+ chalk.gray(` Estimated time at 5k req/hr with concurrency=${concurrency}: ` +
140
+ `~${formatMinutes(pending / (5000 / 60 / 60) / concurrency)}`));
141
+ const workList = [];
142
+ for (const repo of cache.repos) {
143
+ const shas = cache.getCommits(repo.owner, repo.name);
144
+ for (const sha of shas) {
145
+ if (!cache.hasCommitDetail(sha)) {
146
+ workList.push({ owner: repo.owner, repo: repo.name, sha });
147
+ }
148
+ }
149
+ }
150
+ let fetched = 0;
151
+ let errors = 0;
152
+ let saveCounter = 0;
153
+ const bar = ora(`Fetching 0 / ${workList.length}…`).start();
154
+ // Process in batches of `concurrency`
155
+ for (let i = 0; i < workList.length; i += concurrency) {
156
+ const batch = workList.slice(i, i + concurrency);
157
+ const results = await Promise.allSettled(batch.map(({ owner, repo, sha }) => client.fetchCommitDetail(owner, repo, sha)));
158
+ for (let j = 0; j < batch.length; j++) {
159
+ const { sha } = batch[j];
160
+ const result = results[j];
161
+ if (result.status === "fulfilled") {
162
+ cache.setCommitDetail(sha, result.value);
163
+ fetched++;
164
+ }
165
+ else {
166
+ cache.setCommitDetail(sha, null);
167
+ errors++;
168
+ }
169
+ }
170
+ saveCounter += batch.length;
171
+ if (!opts.noCache && saveCounter >= 50) {
172
+ cache.save();
173
+ saveCounter = 0;
174
+ }
175
+ const pct = Math.round(((fetched + errors) / workList.length) * 100);
176
+ const rl = client.getRateLimitInfo();
177
+ bar.text =
178
+ `Fetching ${fetched + errors} / ${workList.length} (${pct}%) — ` +
179
+ chalk.gray(`rate limit: ${rl.remaining} remaining`);
180
+ }
181
+ if (!opts.noCache)
182
+ cache.save();
183
+ bar.succeed(`Fetched ${chalk.bold(fetched)} commits (${errors} errors)`);
184
+ }
185
+ else {
186
+ console.log(`${chalk.green(`✓`)} All commit details already cached (${total} commits)`);
187
+ }
188
+ }
189
+ // ─── Phase 4: Aggregate ───────────────────────────────────────────────────────
190
+ console.log(chalk.gray("\nAggregating…"));
191
+ const { commitsByRepo, commitDetails } = cache.getAggregationData();
192
+ const stats = aggregate(opts.user, commitsByRepo, commitDetails, excludeLanguages);
193
+ const json = JSON.stringify(stats, null, 2);
194
+ if (opts.output) {
195
+ const outPath = resolve(opts.output);
196
+ const outDir = dirname(outPath);
197
+ if (!existsSync(outDir))
198
+ mkdirSync(outDir, { recursive: true });
199
+ writeFileSync(outPath, json, "utf-8");
200
+ console.log(chalk.green(`\n✓ Output written to ${outPath}`));
201
+ }
202
+ else {
203
+ process.stdout.write(`${json}\n`);
204
+ }
205
+ // ─── Summary ─────────────────────────────────────────────────────────────────
206
+ console.log(chalk.bold("\nTop languages (lines changed):"));
207
+ const topN = Object.entries(stats.totals).slice(0, 10);
208
+ const maxLines = topN[0]?.[1] ?? 1;
209
+ for (const [lang, lines] of topN) {
210
+ const bar = "█".repeat(Math.round((lines / maxLines) * 20));
211
+ console.log(` ${lang.padEnd(20)} ${bar.padEnd(20)} ${formatNum(lines)}`);
212
+ }
213
+ console.log(chalk.gray(`\nProcessed ${stats.meta.totalCommitsProcessed} commits across ${stats.meta.totalRepos} repos`));
214
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
215
+ function formatMinutes(minutes) {
216
+ if (minutes < 1)
217
+ return "< 1 min";
218
+ if (minutes < 60)
219
+ return `~${Math.round(minutes)} min`;
220
+ const h = Math.floor(minutes / 60);
221
+ const m = Math.round(minutes % 60);
222
+ return `~${h}h ${m}m`;
223
+ }
224
+ function formatNum(n) {
225
+ return n.toLocaleString("en-US");
226
+ }
227
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function detectLanguage(filename: string): string | null;
2
+ export declare function isExcludedLanguage(language: string): boolean;
3
+ //# sourceMappingURL=language-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language-detector.d.ts","sourceRoot":"","sources":["../src/language-detector.ts"],"names":[],"mappings":"AAgJA,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW9D;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE5D"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Maps file extensions (and special filenames) to GitHub Linguist-style language names.
3
+ * Only includes languages commonly seen in software repos.
4
+ */
5
+ const EXT_MAP = {
6
+ // TypeScript / JavaScript
7
+ ".ts": "TypeScript",
8
+ ".tsx": "TypeScript",
9
+ ".mts": "TypeScript",
10
+ ".cts": "TypeScript",
11
+ ".js": "JavaScript",
12
+ ".jsx": "JavaScript",
13
+ ".mjs": "JavaScript",
14
+ ".cjs": "JavaScript",
15
+ // Web
16
+ ".html": "HTML",
17
+ ".htm": "HTML",
18
+ ".css": "CSS",
19
+ ".scss": "SCSS",
20
+ ".sass": "Sass",
21
+ ".less": "Less",
22
+ ".svelte": "Svelte",
23
+ ".vue": "Vue",
24
+ ".astro": "Astro",
25
+ // Backend / Systems
26
+ ".py": "Python",
27
+ ".rb": "Ruby",
28
+ ".go": "Go",
29
+ ".rs": "Rust",
30
+ ".java": "Java",
31
+ ".kt": "Kotlin",
32
+ ".kts": "Kotlin",
33
+ ".cs": "C#",
34
+ ".cpp": "C++",
35
+ ".cc": "C++",
36
+ ".cxx": "C++",
37
+ ".c": "C",
38
+ ".h": "C",
39
+ ".hpp": "C++",
40
+ ".swift": "Swift",
41
+ ".php": "PHP",
42
+ ".scala": "Scala",
43
+ ".clj": "Clojure",
44
+ ".cljs": "ClojureScript",
45
+ ".ex": "Elixir",
46
+ ".exs": "Elixir",
47
+ ".erl": "Erlang",
48
+ ".hrl": "Erlang",
49
+ ".hs": "Haskell",
50
+ ".lhs": "Haskell",
51
+ ".ml": "OCaml",
52
+ ".mli": "OCaml",
53
+ ".fs": "F#",
54
+ ".fsi": "F#",
55
+ ".fsx": "F#",
56
+ ".dart": "Dart",
57
+ ".lua": "Lua",
58
+ ".r": "R",
59
+ ".R": "R",
60
+ ".m": "MATLAB",
61
+ ".julia": "Julia",
62
+ ".jl": "Julia",
63
+ ".nim": "Nim",
64
+ ".zig": "Zig",
65
+ ".cr": "Crystal",
66
+ ".d": "D",
67
+ // Shell / Scripts
68
+ ".sh": "Shell",
69
+ ".bash": "Shell",
70
+ ".zsh": "Shell",
71
+ ".fish": "Shell",
72
+ ".ps1": "PowerShell",
73
+ ".psm1": "PowerShell",
74
+ ".bat": "Batchfile",
75
+ ".cmd": "Batchfile",
76
+ // Data / Config
77
+ ".json": "JSON",
78
+ ".json5": "JSON5",
79
+ ".jsonc": "JSON",
80
+ ".yaml": "YAML",
81
+ ".yml": "YAML",
82
+ ".toml": "TOML",
83
+ ".xml": "XML",
84
+ ".csv": "CSV",
85
+ ".tsv": "TSV",
86
+ ".ini": "INI",
87
+ ".env": "Shell",
88
+ // Infrastructure / Cloud
89
+ ".tf": "HCL",
90
+ ".tfvars": "HCL",
91
+ ".hcl": "HCL",
92
+ ".bicep": "Bicep",
93
+ ".dockerfile": "Dockerfile",
94
+ // SQL
95
+ ".sql": "SQL",
96
+ ".pgsql": "SQL",
97
+ ".mysql": "SQL",
98
+ // Documentation
99
+ ".md": "Markdown",
100
+ ".mdx": "MDX",
101
+ ".rst": "reStructuredText",
102
+ ".tex": "TeX",
103
+ // GraphQL / API
104
+ ".graphql": "GraphQL",
105
+ ".gql": "GraphQL",
106
+ ".proto": "Protocol Buffer",
107
+ // Other
108
+ ".nix": "Nix",
109
+ ".pkl": "Pkl",
110
+ ".wasm": "WebAssembly",
111
+ ".wat": "WebAssembly"
112
+ };
113
+ /** Special filenames with no extension */
114
+ const FILENAME_MAP = {
115
+ Dockerfile: "Dockerfile",
116
+ Makefile: "Makefile",
117
+ Gemfile: "Ruby",
118
+ Rakefile: "Ruby",
119
+ Podfile: "Ruby",
120
+ Vagrantfile: "Ruby",
121
+ Brewfile: "Ruby",
122
+ ".eslintrc": "JSON",
123
+ ".prettierrc": "JSON",
124
+ ".babelrc": "JSON",
125
+ ".nvmrc": "Shell",
126
+ ".node-version": "Shell"
127
+ };
128
+ /** Languages to exclude from the output (not meaningful for a dev's skill radar) */
129
+ const EXCLUDED_LANGUAGES = new Set([
130
+ "JSON",
131
+ "YAML",
132
+ "TOML",
133
+ "XML",
134
+ "CSV",
135
+ "TSV",
136
+ "INI",
137
+ "Markdown",
138
+ "MDX",
139
+ "reStructuredText",
140
+ "TeX"
141
+ ]);
142
+ export function detectLanguage(filename) {
143
+ // Check special filenames first (basename only)
144
+ const basename = filename.split("/").pop() ?? filename;
145
+ if (FILENAME_MAP[basename])
146
+ return FILENAME_MAP[basename];
147
+ // Get extension
148
+ const dotIndex = basename.lastIndexOf(".");
149
+ if (dotIndex === -1)
150
+ return null;
151
+ const ext = basename.slice(dotIndex).toLowerCase();
152
+ return EXT_MAP[ext] ?? null;
153
+ }
154
+ export function isExcludedLanguage(language) {
155
+ return EXCLUDED_LANGUAGES.has(language);
156
+ }
157
+ //# sourceMappingURL=language-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language-detector.js","sourceRoot":"","sources":["../src/language-detector.ts"],"names":[],"mappings":"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"}
package/dist/lib.d.ts ADDED
@@ -0,0 +1,57 @@
1
+ import type { AggregatedStats } from "./types.js";
2
+ export type { AggregatedStats, CommitDetail, CommitFileDetail, RateLimitInfo, Repo } from "./types.js";
3
+ export interface GetGithubLangStatsOptions {
4
+ /** GitHub username to analyse */
5
+ user: string;
6
+ /** GitHub Personal Access Token (needs repo + read:user scopes) */
7
+ token: string;
8
+ /**
9
+ * Earliest year to include in the analysis.
10
+ * Defaults to **10 years ago** from the current year.
11
+ */
12
+ fromYear?: number;
13
+ /** Languages to exclude (e.g. ["JSON", "YAML"]). */
14
+ excludeLanguages?: string[];
15
+ /** Max concurrent REST requests when fetching commit details. Defaults to 5. */
16
+ concurrency?: number;
17
+ /**
18
+ * Path to the cache file. Defaults to `.github-lang-stats-cache/<user>.json`
19
+ * in the current working directory.
20
+ */
21
+ cachePath?: string;
22
+ /**
23
+ * Set to `false` to disable on-disk caching entirely.
24
+ * Defaults to `true`.
25
+ */
26
+ useCache?: boolean;
27
+ /**
28
+ * Restrict analysis to these repos (`"owner/name"` format).
29
+ * When omitted, all discovered repos are processed.
30
+ */
31
+ repos?: string[];
32
+ /** Optional progress callback invoked as each phase advances. */
33
+ onProgress?: (event: ProgressEvent) => void;
34
+ }
35
+ export type ProgressEvent = {
36
+ phase: "discover";
37
+ details: string;
38
+ } | {
39
+ phase: "shas";
40
+ repo: string;
41
+ count: number;
42
+ } | {
43
+ phase: "details";
44
+ fetched: number;
45
+ total: number;
46
+ } | {
47
+ phase: "aggregate";
48
+ };
49
+ /**
50
+ * Fetch GitHub language statistics for the given user programmatically.
51
+ *
52
+ * The function mirrors the full CLI pipeline (discover repos → collect commit
53
+ * SHAs → fetch commit file details → aggregate) but without any console output
54
+ * or interactive prompts, making it suitable for embedding in other tools.
55
+ */
56
+ export declare function getGithubLangStats(options: GetGithubLangStatsOptions): Promise<AggregatedStats>;
57
+ //# sourceMappingURL=lib.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,YAAY,EACX,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,IAAI,EACJ,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,yBAAyB;IACzC,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,iEAAiE;IACjE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;CAC5C;AAED,MAAM,MAAM,aAAa,GACtB;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC;AAE1B;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACvC,OAAO,EAAE,yBAAyB,GAChC,OAAO,CAAC,eAAe,CAAC,CAuH1B"}
package/dist/lib.js ADDED
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Programmatic API for github-lang-stats.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { getGithubLangStats } from "github-lang-stats";
7
+ *
8
+ * const stats = await getGithubLangStats({
9
+ * user: "octocat",
10
+ * token: process.env.GITHUB_TOKEN!,
11
+ * fromYear: 2020,
12
+ * });
13
+ * console.log(stats.totals);
14
+ * ```
15
+ */
16
+ import { aggregate } from "./aggregator.js";
17
+ import { CacheStore, defaultCachePath } from "./cache.js";
18
+ import { GitHubClient } from "./github-client.js";
19
+ /**
20
+ * Fetch GitHub language statistics for the given user programmatically.
21
+ *
22
+ * The function mirrors the full CLI pipeline (discover repos → collect commit
23
+ * SHAs → fetch commit file details → aggregate) but without any console output
24
+ * or interactive prompts, making it suitable for embedding in other tools.
25
+ */
26
+ export async function getGithubLangStats(options) {
27
+ const { user, token, fromYear = new Date().getFullYear() - 10, excludeLanguages = [], concurrency = 5, cachePath, useCache = true, repos: repoFilter, onProgress } = options;
28
+ const client = new GitHubClient(token);
29
+ const cPath = cachePath ?? defaultCachePath(user);
30
+ const cache = new CacheStore(cPath);
31
+ // ── Phase 1: Discover repos ─────────────────────────────────────────────
32
+ if (cache.repos.length === 0) {
33
+ onProgress?.({ phase: "discover", details: "Discovering repositories…" });
34
+ const allRepos = await client.discoverContributedRepos(user, fromYear, (year) => {
35
+ onProgress?.({
36
+ phase: "discover",
37
+ details: `Scanning contributions for ${year}…`
38
+ });
39
+ });
40
+ cache.repos = allRepos;
41
+ if (useCache)
42
+ cache.save();
43
+ }
44
+ const reposToProcess = repoFilter
45
+ ? cache.repos.filter((r) => repoFilter.includes(`${r.owner}/${r.name}`))
46
+ : cache.repos;
47
+ // ── Phase 2: Collect commit SHAs ────────────────────────────────────────
48
+ const incompleteRepos = reposToProcess.filter((r) => !cache.isRepoComplete(r.owner, r.name));
49
+ if (incompleteRepos.length > 0) {
50
+ const authorId = await client.getUserNodeId(user);
51
+ for (const repo of incompleteRepos) {
52
+ const shas = await client.collectCommitShas(repo.owner, repo.name, authorId, (count) => {
53
+ onProgress?.({
54
+ phase: "shas",
55
+ repo: `${repo.owner}/${repo.name}`,
56
+ count
57
+ });
58
+ }, fromYear);
59
+ cache.addCommits(repo.owner, repo.name, shas);
60
+ cache.markRepoComplete(repo.owner, repo.name);
61
+ if (useCache)
62
+ cache.save();
63
+ }
64
+ }
65
+ const workList = [];
66
+ for (const repo of reposToProcess) {
67
+ const shas = cache.getCommits(repo.owner, repo.name);
68
+ for (const sha of shas) {
69
+ if (!cache.hasCommitDetail(sha)) {
70
+ workList.push({ owner: repo.owner, repo: repo.name, sha });
71
+ }
72
+ }
73
+ }
74
+ let fetched = 0;
75
+ for (let i = 0; i < workList.length; i += concurrency) {
76
+ const batch = workList.slice(i, i + concurrency);
77
+ const results = await Promise.allSettled(batch.map(({ owner, repo, sha }) => client.fetchCommitDetail(owner, repo, sha)));
78
+ for (let j = 0; j < batch.length; j++) {
79
+ const { sha } = batch[j];
80
+ const result = results[j];
81
+ cache.setCommitDetail(sha, result.status === "fulfilled" ? result.value : null);
82
+ fetched++;
83
+ }
84
+ if (useCache)
85
+ cache.save();
86
+ onProgress?.({ phase: "details", fetched, total: workList.length });
87
+ }
88
+ // ── Phase 4: Aggregate ──────────────────────────────────────────────────
89
+ onProgress?.({ phase: "aggregate" });
90
+ const filteredCommitsByRepo = {};
91
+ for (const repo of reposToProcess) {
92
+ const key = `${repo.owner}/${repo.name}`;
93
+ filteredCommitsByRepo[key] = cache.getCommits(repo.owner, repo.name);
94
+ }
95
+ const { commitDetails } = cache.getAggregationData();
96
+ return aggregate(user, filteredCommitsByRepo, commitDetails, excludeLanguages);
97
+ }
98
+ //# sourceMappingURL=lib.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lib.js","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAkDlD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACvC,OAAkC;IAElC,MAAM,EACL,IAAI,EACJ,KAAK,EACL,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE,EACxC,gBAAgB,GAAG,EAAE,EACrB,WAAW,GAAG,CAAC,EACf,SAAS,EACT,QAAQ,GAAG,IAAI,EACf,KAAK,EAAE,UAAU,EACjB,UAAU,EACV,GAAG,OAAO,CAAC;IAEZ,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,SAAS,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IAEpC,2EAA2E;IAE3E,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,2BAA2B,EAAE,CAAC,CAAC;QAC1E,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,wBAAwB,CACrD,IAAI,EACJ,QAAQ,EACR,CAAC,IAAI,EAAE,EAAE;YACR,UAAU,EAAE,CAAC;gBACZ,KAAK,EAAE,UAAU;gBACjB,OAAO,EAAE,8BAA8B,IAAI,GAAG;aAC9C,CAAC,CAAC;QACJ,CAAC,CACD,CAAC;QACF,KAAK,CAAC,KAAK,GAAG,QAAQ,CAAC;QACvB,IAAI,QAAQ;YAAE,KAAK,CAAC,IAAI,EAAE,CAAC;IAC5B,CAAC;IAED,MAAM,cAAc,GAAG,UAAU;QAChC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACxE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;IAEf,2EAA2E;IAE3E,MAAM,eAAe,GAAG,cAAc,CAAC,MAAM,CAC5C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAC7C,CAAC;IAEF,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAElD,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;YACpC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAC1C,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,IAAI,EACT,QAAQ,EACR,CAAC,KAAK,EAAE,EAAE;gBACT,UAAU,EAAE,CAAC;oBACZ,KAAK,EAAE,MAAM;oBACb,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE;oBAClC,KAAK;iBACL,CAAC,CAAC;YACJ,CAAC,EACD,QAAQ,CACR,CAAC;YACF,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC9C,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,IAAI,QAAQ;gBAAE,KAAK,CAAC,IAAI,EAAE,CAAC;QAC5B,CAAC;IACF,CAAC;IAKD,MAAM,QAAQ,GAAW,EAAE,CAAC;IAC5B,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACrD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACxB,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;gBACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;YAC5D,CAAC;QACF,CAAC;IACF,CAAC;IAED,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,WAAW,EAAE,CAAC;QACvD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CACvC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE,CAClC,MAAM,CAAC,iBAAiB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,CAC1C,CACD,CAAC;QACF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACzB,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YAC1B,KAAK,CAAC,eAAe,CACpB,GAAG,EACH,MAAM,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CACnD,CAAC;YACF,OAAO,EAAE,CAAC;QACX,CAAC;QACD,IAAI,QAAQ;YAAE,KAAK,CAAC,IAAI,EAAE,CAAC;QAC3B,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,2EAA2E;IAE3E,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IAErC,MAAM,qBAAqB,GAA6B,EAAE,CAAC;IAC3D,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACzC,qBAAqB,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC,kBAAkB,EAAE,CAAC;IAErD,OAAO,SAAS,CACf,IAAI,EACJ,qBAAqB,EACrB,aAAa,EACb,gBAAgB,CAChB,CAAC;AACH,CAAC"}