@ossintel/github-normalizer 0.0.0 → 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.
- package/LICENSE +1 -1
- package/README.md +3 -2
- package/dist/index.d.mts +30 -1
- package/dist/index.d.ts +30 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +15 -10
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -82,8 +82,9 @@ Never expose raw GitHub responses.
|
|
|
82
82
|
|
|
83
83
|
## ✨ Why @ossintel/github-normalizer?
|
|
84
84
|
|
|
85
|
-
-
|
|
86
|
-
-
|
|
85
|
+
- **Consistent Domain Model**: Normalizes disparate GitHub REST and GraphQL responses into a single, clean TypeScript interface.
|
|
86
|
+
- **Auto Pagination & Rate Limit Handling**: Simplifies fetching multi-page repository lists and respects API rate limits transparently.
|
|
87
|
+
- **Zero Ecosystem Pollution**: Keeps business logic, scoring, and UI code out of the network fetching layer.
|
|
87
88
|
|
|
88
89
|
---
|
|
89
90
|
|
package/dist/index.d.mts
CHANGED
|
@@ -123,6 +123,7 @@ interface NormalizedDeveloper {
|
|
|
123
123
|
following: number;
|
|
124
124
|
createdAt: string;
|
|
125
125
|
updatedAt: string;
|
|
126
|
+
socialLinks?: string[];
|
|
126
127
|
}
|
|
127
128
|
interface NormalizedRepository {
|
|
128
129
|
id: number;
|
|
@@ -216,6 +217,33 @@ interface LinkedIdentitySuggestions {
|
|
|
216
217
|
url: string;
|
|
217
218
|
};
|
|
218
219
|
}
|
|
220
|
+
interface RawGitHubSearchIssue {
|
|
221
|
+
title: string;
|
|
222
|
+
html_url: string;
|
|
223
|
+
repository_url: string;
|
|
224
|
+
number: number;
|
|
225
|
+
state: string;
|
|
226
|
+
created_at: string;
|
|
227
|
+
closed_at: string | null;
|
|
228
|
+
pull_request?: {
|
|
229
|
+
merged_at?: string | null;
|
|
230
|
+
};
|
|
231
|
+
labels: {
|
|
232
|
+
name: string;
|
|
233
|
+
}[];
|
|
234
|
+
}
|
|
235
|
+
interface NormalizedContribution {
|
|
236
|
+
title: string;
|
|
237
|
+
htmlUrl: string;
|
|
238
|
+
repoFullName: string;
|
|
239
|
+
number: number;
|
|
240
|
+
state: string;
|
|
241
|
+
createdAt: string;
|
|
242
|
+
mergedAt: string | null;
|
|
243
|
+
labels: string[];
|
|
244
|
+
type: "code" | "docs" | "test" | "chore";
|
|
245
|
+
targetRepoStars: number;
|
|
246
|
+
}
|
|
219
247
|
|
|
220
248
|
declare function fetchDeveloper(username?: string, options?: GitHubFetchOptions): Promise<NormalizedDeveloper>;
|
|
221
249
|
declare function fetchRepositories(owner?: string, options?: GitHubFetchOptions): Promise<NormalizedRepository[]>;
|
|
@@ -227,5 +255,6 @@ declare function fetchReleases(owner: string, repo: string, options?: GitHubFetc
|
|
|
227
255
|
declare function fetchOrganization(login: string, options?: GitHubFetchOptions): Promise<NormalizedOrganization>;
|
|
228
256
|
declare function detectInput(input: string): InputDetectionResult;
|
|
229
257
|
declare function suggestLinkedIdentities(developer: NormalizedDeveloper, _repositories: NormalizedRepository[]): LinkedIdentitySuggestions;
|
|
258
|
+
declare function fetchExternalContributions(username: string, limit?: number, options?: GitHubFetchOptions): Promise<NormalizedContribution[]>;
|
|
230
259
|
|
|
231
|
-
export { type GitHubFetchOptions, GitHubHttpError, GitHubRateLimitError, type InputDetectionResult, type LinkedIdentitySuggestions, type NormalizedContributor, type NormalizedDeveloper, type NormalizedLanguage, type NormalizedOrganization, type NormalizedRelease, type NormalizedRepository, type RawGitHubContributor, type RawGitHubOrganization, type RawGitHubRelease, type RawGitHubRepository, type RawGitHubUser, detectInput, fetchContributors, fetchDeveloper, fetchLanguages, fetchOrganization, fetchOrganizations, fetchReleases, fetchRepositories, fetchRepository, suggestLinkedIdentities };
|
|
260
|
+
export { type GitHubFetchOptions, GitHubHttpError, GitHubRateLimitError, type InputDetectionResult, type LinkedIdentitySuggestions, type NormalizedContribution, type NormalizedContributor, type NormalizedDeveloper, type NormalizedLanguage, type NormalizedOrganization, type NormalizedRelease, type NormalizedRepository, type RawGitHubContributor, type RawGitHubOrganization, type RawGitHubRelease, type RawGitHubRepository, type RawGitHubSearchIssue, type RawGitHubUser, detectInput, fetchContributors, fetchDeveloper, fetchExternalContributions, fetchLanguages, fetchOrganization, fetchOrganizations, fetchReleases, fetchRepositories, fetchRepository, suggestLinkedIdentities };
|
package/dist/index.d.ts
CHANGED
|
@@ -123,6 +123,7 @@ interface NormalizedDeveloper {
|
|
|
123
123
|
following: number;
|
|
124
124
|
createdAt: string;
|
|
125
125
|
updatedAt: string;
|
|
126
|
+
socialLinks?: string[];
|
|
126
127
|
}
|
|
127
128
|
interface NormalizedRepository {
|
|
128
129
|
id: number;
|
|
@@ -216,6 +217,33 @@ interface LinkedIdentitySuggestions {
|
|
|
216
217
|
url: string;
|
|
217
218
|
};
|
|
218
219
|
}
|
|
220
|
+
interface RawGitHubSearchIssue {
|
|
221
|
+
title: string;
|
|
222
|
+
html_url: string;
|
|
223
|
+
repository_url: string;
|
|
224
|
+
number: number;
|
|
225
|
+
state: string;
|
|
226
|
+
created_at: string;
|
|
227
|
+
closed_at: string | null;
|
|
228
|
+
pull_request?: {
|
|
229
|
+
merged_at?: string | null;
|
|
230
|
+
};
|
|
231
|
+
labels: {
|
|
232
|
+
name: string;
|
|
233
|
+
}[];
|
|
234
|
+
}
|
|
235
|
+
interface NormalizedContribution {
|
|
236
|
+
title: string;
|
|
237
|
+
htmlUrl: string;
|
|
238
|
+
repoFullName: string;
|
|
239
|
+
number: number;
|
|
240
|
+
state: string;
|
|
241
|
+
createdAt: string;
|
|
242
|
+
mergedAt: string | null;
|
|
243
|
+
labels: string[];
|
|
244
|
+
type: "code" | "docs" | "test" | "chore";
|
|
245
|
+
targetRepoStars: number;
|
|
246
|
+
}
|
|
219
247
|
|
|
220
248
|
declare function fetchDeveloper(username?: string, options?: GitHubFetchOptions): Promise<NormalizedDeveloper>;
|
|
221
249
|
declare function fetchRepositories(owner?: string, options?: GitHubFetchOptions): Promise<NormalizedRepository[]>;
|
|
@@ -227,5 +255,6 @@ declare function fetchReleases(owner: string, repo: string, options?: GitHubFetc
|
|
|
227
255
|
declare function fetchOrganization(login: string, options?: GitHubFetchOptions): Promise<NormalizedOrganization>;
|
|
228
256
|
declare function detectInput(input: string): InputDetectionResult;
|
|
229
257
|
declare function suggestLinkedIdentities(developer: NormalizedDeveloper, _repositories: NormalizedRepository[]): LinkedIdentitySuggestions;
|
|
258
|
+
declare function fetchExternalContributions(username: string, limit?: number, options?: GitHubFetchOptions): Promise<NormalizedContribution[]>;
|
|
230
259
|
|
|
231
|
-
export { type GitHubFetchOptions, GitHubHttpError, GitHubRateLimitError, type InputDetectionResult, type LinkedIdentitySuggestions, type NormalizedContributor, type NormalizedDeveloper, type NormalizedLanguage, type NormalizedOrganization, type NormalizedRelease, type NormalizedRepository, type RawGitHubContributor, type RawGitHubOrganization, type RawGitHubRelease, type RawGitHubRepository, type RawGitHubUser, detectInput, fetchContributors, fetchDeveloper, fetchLanguages, fetchOrganization, fetchOrganizations, fetchReleases, fetchRepositories, fetchRepository, suggestLinkedIdentities };
|
|
260
|
+
export { type GitHubFetchOptions, GitHubHttpError, GitHubRateLimitError, type InputDetectionResult, type LinkedIdentitySuggestions, type NormalizedContribution, type NormalizedContributor, type NormalizedDeveloper, type NormalizedLanguage, type NormalizedOrganization, type NormalizedRelease, type NormalizedRepository, type RawGitHubContributor, type RawGitHubOrganization, type RawGitHubRelease, type RawGitHubRepository, type RawGitHubSearchIssue, type RawGitHubUser, detectInput, fetchContributors, fetchDeveloper, fetchExternalContributions, fetchLanguages, fetchOrganization, fetchOrganizations, fetchReleases, fetchRepositories, fetchRepository, suggestLinkedIdentities };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var R=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var O=(t,e)=>{for(var r in e)R(t,r,{get:e[r],enumerable:!0})},v=(t,e,r,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of k(e))!I.call(t,n)&&n!==r&&R(t,n,{get:()=>e[n],enumerable:!(i=N(e,n))||i.enumerable});return t};var A=t=>v(R({},"__esModule",{value:!0}),t);var X={};O(X,{GitHubHttpError:()=>h,GitHubRateLimitError:()=>f,detectInput:()=>M,fetchContributors:()=>B,fetchDeveloper:()=>L,fetchExternalContributions:()=>V,fetchLanguages:()=>q,fetchOrganization:()=>E,fetchOrganizations:()=>S,fetchReleases:()=>j,fetchRepositories:()=>W,fetchRepository:()=>D,suggestLinkedIdentities:()=>K});module.exports=A(X);var h=class extends Error{status;statusText;constructor(e,r,i){super(i),this.name="GitHubHttpError",this.status=e,this.statusText=r}},f=class extends Error{limit;remaining;resetTime;constructor(e,r,i,n){super(n),this.name="GitHubRateLimitError",this.limit=e,this.remaining=r,this.resetTime=i}};function w(t){let e=t?.token??process.env.GITHUB_TOKEN??process.env.GITHUB_PAT,r=t?.baseUrl??"https://api.github.com",i={Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28"};return e&&(i.Authorization=`Bearer ${e}`),{baseUrl:r,headers:i}}async function z(t,e){let r=await fetch(t,{headers:e}),i=r.headers.get("x-ratelimit-limit"),n=r.headers.get("x-ratelimit-remaining"),a=r.headers.get("x-ratelimit-reset");if((r.status===403||r.status===429)&&n==="0"&&a){let l=i?Number.parseInt(i,10):0,m=Number.parseInt(n,10),u=new Date(Number.parseInt(a,10)*1e3);throw new f(l,m,u,`GitHub API Rate Limit Exceeded. Resets at ${u.toISOString()}`)}if(!r.ok){let l=await r.text().catch(()=>"");throw new h(r.status,r.statusText,`GitHub API request failed with status ${r.status}: ${l||r.statusText}`)}return r}async function p(t,e){let{baseUrl:r,headers:i}=w(e),n=t.startsWith("http://")||t.startsWith("https://")?t:`${r}${t.startsWith("/")?"":"/"}${t}`;return(await z(n,i)).json()}async function y(t,e){let{baseUrl:r,headers:i}=w(e),n=[],a=null,l=e?.perPage??100,m=e?.page,u=t;if(!u.startsWith("http://")&&!u.startsWith("https://")){let o=u.includes("?")?"&":"?",s=[];l!==void 0&&s.push(`per_page=${l}`),m!==void 0&&s.push(`page=${m}`),s.length>0&&(u=`${u}${o}${s.join("&")}`)}do{let o=a??(u.startsWith("http://")||u.startsWith("https://")?u:`${r}${u.startsWith("/")?"":"/"}${u}`),s=await z(o,i),d=await s.json();if(Array.isArray(d))n.push(...d);else break;if(e?.allPages===!1||m!==void 0)break;let c=s.headers.get("link");if(c){let b=c.match(/<([^>]+)>;\s*rel="next"/);a=b?b[1]:null}else a=null}while(a);return n}function C(t){return{id:t.id,login:t.login,name:t.name??null,avatarUrl:t.avatar_url,htmlUrl:t.html_url,type:t.type,company:t.company??null,blog:t.blog??null,location:t.location??null,email:t.email??null,bio:t.bio??null,twitterUsername:t.twitter_username??null,publicRepos:t.public_repos??0,publicGists:t.public_gists??0,followers:t.followers??0,following:t.following??0,createdAt:t.created_at,updatedAt:t.updated_at}}function H(t){return{id:t.id,name:t.name,fullName:t.full_name,owner:{login:t.owner?.login??"",id:t.owner?.id??0,avatarUrl:t.owner?.avatar_url??"",type:t.owner?.type??""},htmlUrl:t.html_url,description:t.description??null,isFork:t.fork??!1,createdAt:t.created_at,updatedAt:t.updated_at,pushedAt:t.pushed_at,homepage:t.homepage??null,size:t.size??0,stargazersCount:t.stargazers_count??0,watchersCount:t.watchers_count??0,language:t.language??null,forksCount:t.forks_count??0,openIssuesCount:t.open_issues_count??0,defaultBranch:t.default_branch??"main",topics:t.topics??[],isArchived:t.archived??!1}}function G(t){return{id:t.id,login:t.login,name:t.name??null,description:t.description??null,avatarUrl:t.avatar_url,htmlUrl:t.html_url,location:t.location??null,email:t.email??null,blog:t.blog??null,twitterUsername:t.twitter_username??null,publicRepos:t.public_repos??0,followers:t.followers??0,createdAt:t.created_at,updatedAt:t.updated_at}}function F(t){return{id:t.id,login:t.login,avatarUrl:t.avatar_url,htmlUrl:t.html_url,type:t.type??"User",contributions:t.contributions??0}}function P(t){return{id:t.id,name:t.name??null,tagName:t.tag_name,targetCommitish:t.target_commitish,body:t.body??null,draft:t.draft??!1,prerelease:t.prerelease??!1,createdAt:t.created_at,publishedAt:t.published_at??null,htmlUrl:t.html_url,author:t.author?{login:t.author.login,id:t.author.id,avatarUrl:t.author.avatar_url}:null}}async function T(t,e){try{return(await p(`/users/${t}`,e)).type==="Organization"?"Organization":"User"}catch{return"User"}}async function L(t,e){let r=t?`/users/${t}`:"/user",i=await p(r,e);if(i.type==="Organization")throw new Error(`Requested entity '${t||"authenticated user"}' is an Organization, not a User.`);let n=[];try{let l=t||i.login;n=(await p(`/users/${l}/social_accounts`,e)||[]).map(u=>u.url)}catch{}return{...C(i),socialLinks:n}}async function W(t,e){let r;return t?r=await T(t,e)==="Organization"?`/orgs/${t}/repos`:`/users/${t}/repos`:r="/user/repos",(await y(r,e)).map(H)}async function D(t,e,r){let i=await p(`/repos/${t}/${e}`,r);return H(i)}async function S(t,e){let r=t?`/users/${t}/orgs`:"/user/orgs",i=await y(r,e);return(await Promise.all(i.map(a=>p(`/orgs/${a.login}`,e)))).map(G)}async function B(t,e,r){return(await y(`/repos/${t}/${e}/contributors`,r)).map(F)}async function q(t,e,r){let i=await p(`/repos/${t}/${e}/languages`,r);return Object.entries(i).map(([n,a])=>({name:n,bytes:a}))}async function j(t,e,r){return(await y(`/repos/${t}/${e}/releases`,r)).map(P)}async function E(t,e){let r=await p(`/orgs/${t}`,e);return G(r)}function M(t){let e=t.trim();if(e.startsWith("npm:pkg:"))return{platform:"npm",type:"package",name:e.slice(8),rawInput:e};if(e.startsWith("npm:"))return{platform:"npm",type:"user",name:e.slice(4),rawInput:e};if(e.startsWith("~"))return{platform:"npm",type:"user",name:e.slice(1),rawInput:e};if(e.startsWith("so:")||e.startsWith("stackoverflow:")){let r=e.startsWith("so:")?3:14;return{platform:"stackoverflow",type:"user",profileId:e.slice(r),rawInput:e}}try{let r=new URL(e.startsWith("http")?e:`https://${e}`),i=r.hostname.toLowerCase();if(i.includes("github.com")){let n=r.pathname.split("/").filter(Boolean);if(n.length===1)return{platform:"github",type:"unknown",owner:n[0],rawInput:e};if(n.length>=2)return{platform:"github",type:"repo",owner:n[0],repo:n[1],rawInput:e}}if(i.includes("npmjs.com")){let n=r.pathname.split("/").filter(Boolean);if(n[0]==="package")return{platform:"npm",type:"package",name:n.slice(1).join("/"),rawInput:e};if(n[0].startsWith("~"))return{platform:"npm",type:"user",name:n[0].slice(1),rawInput:e};if(n[0].startsWith("@"))return n.length===1?{platform:"npm",type:"org",name:n[0],rawInput:e}:{platform:"npm",type:"package",name:`${n[0]}/${n[1]}`,rawInput:e}}if(i.includes("stackoverflow.com")){let n=r.pathname.split("/").filter(Boolean);if(n[0]==="users"&&n[1])return{platform:"stackoverflow",type:"user",profileId:n[1],name:n[2]||void 0,rawInput:e}}}catch{}if(e.includes("/")){let r=e.split("/");return{platform:"github",type:"repo",owner:r[0],repo:r[1],rawInput:e}}return{platform:"github",type:"unknown",owner:e,rawInput:e}}function K(t,e){let r={},i=[t.blog,t.bio,t.company,...t.socialLinks||[]].filter(Boolean),n=/stackoverflow\.com\/users\/(\d+)\/([a-zA-Z0-9_-]+)?/;for(let a of i){let l=a.match(n);if(l){r.stackoverflow={profileId:l[1],displayName:l[2]||t.login,url:`https://stackoverflow.com/users/${l[1]}/${l[2]||""}`};break}}return r.npm={username:t.login,url:`https://www.npmjs.com/~${t.login}`},r}async function V(t,e=10,r){try{let i=[],n=1,a=!0;for(;a;){let s=(await p(`/search/issues?q=type:pr+author:${t}+-user:${t}+is:merged&per_page=100&page=${n}`,r)).items||[];s.length===0?a=!1:(i.push(...s),s.length<100?a=!1:n++),n>10&&(a=!1)}let l=new Map,m=Array.from(new Set(i.map(o=>{let s=o.repository_url.split("/");return`${s[s.length-2]}/${s[s.length-1]}`}))).slice(0,e);for(let o of m)try{let s=await p(`/repos/${o}`,r);l.set(o,s.stargazers_count??0)}catch(s){console.error(`Failed to fetch repo metadata for ${o}`,s),l.set(o,0)}let u=[];for(let o of i){let s=o.repository_url.split("/"),d=`${s[s.length-2]}/${s[s.length-1]}`;if(!l.has(d))continue;let c=o.title.toLowerCase(),b=(o.labels||[]).map(g=>g.name.toLowerCase()),_="code",x=c.includes("typo")||c.includes("readme")||c.includes("docs:")||c.includes("documentation")||c.includes("spelling")||b.some(g=>g.includes("doc")||g.includes("typo")||g.includes("readme")),$=c.includes("test:")||c.includes("tests:")||c.includes("spec:")||c.includes("unit test")||b.some(g=>g.includes("test")||g.includes("spec")||g.includes("qa")),U=c.includes("chore:")||c.includes("bump ")||c.includes("release ")||b.some(g=>g.includes("chore")||g.includes("dependencies")||g.includes("ci"));x?_="docs":$?_="test":U&&(_="chore"),u.push({title:o.title,htmlUrl:o.html_url,repoFullName:d,number:o.number,state:o.state,createdAt:o.created_at,mergedAt:o.pull_request?.merged_at||o.closed_at,labels:(o.labels||[]).map(g=>g.name),type:_,targetRepoStars:l.get(d)||0})}return u}catch(i){return console.error("Failed to fetch external contributions",i),[]}}0&&(module.exports={GitHubHttpError,GitHubRateLimitError,detectInput,fetchContributors,fetchDeveloper,fetchExternalContributions,fetchLanguages,fetchOrganization,fetchOrganizations,fetchReleases,fetchRepositories,fetchRepository,suggestLinkedIdentities});
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var y=class extends Error{status;statusText;constructor(e,r,i){super(i),this.name="GitHubHttpError",this.status=e,this.statusText=r}},_=class extends Error{limit;remaining;resetTime;constructor(e,r,i,n){super(n),this.name="GitHubRateLimitError",this.limit=e,this.remaining=r,this.resetTime=i}};function R(t){let e=t?.token??process.env.GITHUB_TOKEN??process.env.GITHUB_PAT,r=t?.baseUrl??"https://api.github.com",i={Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28"};return e&&(i.Authorization=`Bearer ${e}`),{baseUrl:r,headers:i}}async function w(t,e){let r=await fetch(t,{headers:e}),i=r.headers.get("x-ratelimit-limit"),n=r.headers.get("x-ratelimit-remaining"),a=r.headers.get("x-ratelimit-reset");if((r.status===403||r.status===429)&&n==="0"&&a){let l=i?Number.parseInt(i,10):0,m=Number.parseInt(n,10),u=new Date(Number.parseInt(a,10)*1e3);throw new _(l,m,u,`GitHub API Rate Limit Exceeded. Resets at ${u.toISOString()}`)}if(!r.ok){let l=await r.text().catch(()=>"");throw new y(r.status,r.statusText,`GitHub API request failed with status ${r.status}: ${l||r.statusText}`)}return r}async function p(t,e){let{baseUrl:r,headers:i}=R(e),n=t.startsWith("http://")||t.startsWith("https://")?t:`${r}${t.startsWith("/")?"":"/"}${t}`;return(await w(n,i)).json()}async function h(t,e){let{baseUrl:r,headers:i}=R(e),n=[],a=null,l=e?.perPage??100,m=e?.page,u=t;if(!u.startsWith("http://")&&!u.startsWith("https://")){let o=u.includes("?")?"&":"?",s=[];l!==void 0&&s.push(`per_page=${l}`),m!==void 0&&s.push(`page=${m}`),s.length>0&&(u=`${u}${o}${s.join("&")}`)}do{let o=a??(u.startsWith("http://")||u.startsWith("https://")?u:`${r}${u.startsWith("/")?"":"/"}${u}`),s=await w(o,i),d=await s.json();if(Array.isArray(d))n.push(...d);else break;if(e?.allPages===!1||m!==void 0)break;let c=s.headers.get("link");if(c){let b=c.match(/<([^>]+)>;\s*rel="next"/);a=b?b[1]:null}else a=null}while(a);return n}function U(t){return{id:t.id,login:t.login,name:t.name??null,avatarUrl:t.avatar_url,htmlUrl:t.html_url,type:t.type,company:t.company??null,blog:t.blog??null,location:t.location??null,email:t.email??null,bio:t.bio??null,twitterUsername:t.twitter_username??null,publicRepos:t.public_repos??0,publicGists:t.public_gists??0,followers:t.followers??0,following:t.following??0,createdAt:t.created_at,updatedAt:t.updated_at}}function z(t){return{id:t.id,name:t.name,fullName:t.full_name,owner:{login:t.owner?.login??"",id:t.owner?.id??0,avatarUrl:t.owner?.avatar_url??"",type:t.owner?.type??""},htmlUrl:t.html_url,description:t.description??null,isFork:t.fork??!1,createdAt:t.created_at,updatedAt:t.updated_at,pushedAt:t.pushed_at,homepage:t.homepage??null,size:t.size??0,stargazersCount:t.stargazers_count??0,watchersCount:t.watchers_count??0,language:t.language??null,forksCount:t.forks_count??0,openIssuesCount:t.open_issues_count??0,defaultBranch:t.default_branch??"main",topics:t.topics??[],isArchived:t.archived??!1}}function H(t){return{id:t.id,login:t.login,name:t.name??null,description:t.description??null,avatarUrl:t.avatar_url,htmlUrl:t.html_url,location:t.location??null,email:t.email??null,blog:t.blog??null,twitterUsername:t.twitter_username??null,publicRepos:t.public_repos??0,followers:t.followers??0,createdAt:t.created_at,updatedAt:t.updated_at}}function N(t){return{id:t.id,login:t.login,avatarUrl:t.avatar_url,htmlUrl:t.html_url,type:t.type??"User",contributions:t.contributions??0}}function k(t){return{id:t.id,name:t.name??null,tagName:t.tag_name,targetCommitish:t.target_commitish,body:t.body??null,draft:t.draft??!1,prerelease:t.prerelease??!1,createdAt:t.created_at,publishedAt:t.published_at??null,htmlUrl:t.html_url,author:t.author?{login:t.author.login,id:t.author.id,avatarUrl:t.author.avatar_url}:null}}async function I(t,e){try{return(await p(`/users/${t}`,e)).type==="Organization"?"Organization":"User"}catch{return"User"}}async function F(t,e){let r=t?`/users/${t}`:"/user",i=await p(r,e);if(i.type==="Organization")throw new Error(`Requested entity '${t||"authenticated user"}' is an Organization, not a User.`);let n=[];try{let l=t||i.login;n=(await p(`/users/${l}/social_accounts`,e)||[]).map(u=>u.url)}catch{}return{...U(i),socialLinks:n}}async function P(t,e){let r;return t?r=await I(t,e)==="Organization"?`/orgs/${t}/repos`:`/users/${t}/repos`:r="/user/repos",(await h(r,e)).map(z)}async function T(t,e,r){let i=await p(`/repos/${t}/${e}`,r);return z(i)}async function L(t,e){let r=t?`/users/${t}/orgs`:"/user/orgs",i=await h(r,e);return(await Promise.all(i.map(a=>p(`/orgs/${a.login}`,e)))).map(H)}async function W(t,e,r){return(await h(`/repos/${t}/${e}/contributors`,r)).map(N)}async function D(t,e,r){let i=await p(`/repos/${t}/${e}/languages`,r);return Object.entries(i).map(([n,a])=>({name:n,bytes:a}))}async function S(t,e,r){return(await h(`/repos/${t}/${e}/releases`,r)).map(k)}async function B(t,e){let r=await p(`/orgs/${t}`,e);return H(r)}function q(t){let e=t.trim();if(e.startsWith("npm:pkg:"))return{platform:"npm",type:"package",name:e.slice(8),rawInput:e};if(e.startsWith("npm:"))return{platform:"npm",type:"user",name:e.slice(4),rawInput:e};if(e.startsWith("~"))return{platform:"npm",type:"user",name:e.slice(1),rawInput:e};if(e.startsWith("so:")||e.startsWith("stackoverflow:")){let r=e.startsWith("so:")?3:14;return{platform:"stackoverflow",type:"user",profileId:e.slice(r),rawInput:e}}try{let r=new URL(e.startsWith("http")?e:`https://${e}`),i=r.hostname.toLowerCase();if(i.includes("github.com")){let n=r.pathname.split("/").filter(Boolean);if(n.length===1)return{platform:"github",type:"unknown",owner:n[0],rawInput:e};if(n.length>=2)return{platform:"github",type:"repo",owner:n[0],repo:n[1],rawInput:e}}if(i.includes("npmjs.com")){let n=r.pathname.split("/").filter(Boolean);if(n[0]==="package")return{platform:"npm",type:"package",name:n.slice(1).join("/"),rawInput:e};if(n[0].startsWith("~"))return{platform:"npm",type:"user",name:n[0].slice(1),rawInput:e};if(n[0].startsWith("@"))return n.length===1?{platform:"npm",type:"org",name:n[0],rawInput:e}:{platform:"npm",type:"package",name:`${n[0]}/${n[1]}`,rawInput:e}}if(i.includes("stackoverflow.com")){let n=r.pathname.split("/").filter(Boolean);if(n[0]==="users"&&n[1])return{platform:"stackoverflow",type:"user",profileId:n[1],name:n[2]||void 0,rawInput:e}}}catch{}if(e.includes("/")){let r=e.split("/");return{platform:"github",type:"repo",owner:r[0],repo:r[1],rawInput:e}}return{platform:"github",type:"unknown",owner:e,rawInput:e}}function j(t,e){let r={},i=[t.blog,t.bio,t.company,...t.socialLinks||[]].filter(Boolean),n=/stackoverflow\.com\/users\/(\d+)\/([a-zA-Z0-9_-]+)?/;for(let a of i){let l=a.match(n);if(l){r.stackoverflow={profileId:l[1],displayName:l[2]||t.login,url:`https://stackoverflow.com/users/${l[1]}/${l[2]||""}`};break}}return r.npm={username:t.login,url:`https://www.npmjs.com/~${t.login}`},r}async function E(t,e=10,r){try{let i=[],n=1,a=!0;for(;a;){let s=(await p(`/search/issues?q=type:pr+author:${t}+-user:${t}+is:merged&per_page=100&page=${n}`,r)).items||[];s.length===0?a=!1:(i.push(...s),s.length<100?a=!1:n++),n>10&&(a=!1)}let l=new Map,m=Array.from(new Set(i.map(o=>{let s=o.repository_url.split("/");return`${s[s.length-2]}/${s[s.length-1]}`}))).slice(0,e);for(let o of m)try{let s=await p(`/repos/${o}`,r);l.set(o,s.stargazers_count??0)}catch(s){console.error(`Failed to fetch repo metadata for ${o}`,s),l.set(o,0)}let u=[];for(let o of i){let s=o.repository_url.split("/"),d=`${s[s.length-2]}/${s[s.length-1]}`;if(!l.has(d))continue;let c=o.title.toLowerCase(),b=(o.labels||[]).map(g=>g.name.toLowerCase()),f="code",G=c.includes("typo")||c.includes("readme")||c.includes("docs:")||c.includes("documentation")||c.includes("spelling")||b.some(g=>g.includes("doc")||g.includes("typo")||g.includes("readme")),x=c.includes("test:")||c.includes("tests:")||c.includes("spec:")||c.includes("unit test")||b.some(g=>g.includes("test")||g.includes("spec")||g.includes("qa")),$=c.includes("chore:")||c.includes("bump ")||c.includes("release ")||b.some(g=>g.includes("chore")||g.includes("dependencies")||g.includes("ci"));G?f="docs":x?f="test":$&&(f="chore"),u.push({title:o.title,htmlUrl:o.html_url,repoFullName:d,number:o.number,state:o.state,createdAt:o.created_at,mergedAt:o.pull_request?.merged_at||o.closed_at,labels:(o.labels||[]).map(g=>g.name),type:f,targetRepoStars:l.get(d)||0})}return u}catch(i){return console.error("Failed to fetch external contributions",i),[]}}export{y as GitHubHttpError,_ as GitHubRateLimitError,q as detectInput,W as fetchContributors,F as fetchDeveloper,E as fetchExternalContributions,D as fetchLanguages,B as fetchOrganization,L as fetchOrganizations,S as fetchReleases,P as fetchRepositories,T as fetchRepository,j as suggestLinkedIdentities};
|
package/package.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"name": "@ossintel/github-normalizer",
|
|
3
3
|
"author": "Mayank Kumar Chaudhari <https://mayankchaudhari.com>",
|
|
4
4
|
"private": false,
|
|
5
|
-
"version": "0.
|
|
6
|
-
"description": "
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"description": "Robust, typed utilities to fetch, normalize, and sanitize raw GitHub REST and GraphQL API responses into consistent domain structures for open-source analytics.",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"main": "./dist/index.js",
|
|
9
9
|
"module": "./dist/index.mjs",
|
|
@@ -35,26 +35,31 @@
|
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "latest",
|
|
37
37
|
"tsup": "latest",
|
|
38
|
-
"typescript": "
|
|
38
|
+
"typescript": "^6.0.3"
|
|
39
39
|
},
|
|
40
40
|
"forge": {
|
|
41
|
-
"aliases": [
|
|
41
|
+
"aliases": [
|
|
42
|
+
"@ossintel/github",
|
|
43
|
+
"github-normalizer"
|
|
44
|
+
],
|
|
42
45
|
"icon": "LibraryBig",
|
|
43
46
|
"description": ""
|
|
44
47
|
},
|
|
45
48
|
"funding": [
|
|
46
|
-
{
|
|
47
|
-
"type": "github",
|
|
48
|
-
"url": "https://github.com/sponsors/mayank1513"
|
|
49
|
-
},
|
|
50
49
|
{
|
|
51
50
|
"type": "github",
|
|
52
51
|
"url": "https://github.com/sponsors/mayank1513"
|
|
53
52
|
}
|
|
54
53
|
],
|
|
55
54
|
"keywords": [
|
|
56
|
-
"
|
|
57
|
-
"
|
|
55
|
+
"github-api",
|
|
56
|
+
"github-graphql",
|
|
57
|
+
"data-normalization",
|
|
58
|
+
"open-source",
|
|
59
|
+
"analytics",
|
|
60
|
+
"ossintel",
|
|
61
|
+
"typescript",
|
|
62
|
+
"fetch"
|
|
58
63
|
],
|
|
59
64
|
"scripts": {
|
|
60
65
|
"build": "tsup && gzip -c dist/index.js | wc -c",
|