@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 turboforge-dev
3
+ Copyright (c) 2026 ossintel
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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 d=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames;var x=Object.prototype.hasOwnProperty;var U=(t,e)=>{for(var n in e)d(t,n,{get:e[n],enumerable:!0})},O=(t,e,n,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of G(e))!x.call(t,r)&&r!==n&&d(t,r,{get:()=>e[r],enumerable:!(i=H(e,r))||i.enumerable});return t};var k=t=>O(d({},"__esModule",{value:!0}),t);var E={};U(E,{GitHubHttpError:()=>p,GitHubRateLimitError:()=>c,detectInput:()=>B,fetchContributors:()=>F,fetchDeveloper:()=>A,fetchLanguages:()=>D,fetchOrganization:()=>W,fetchOrganizations:()=>T,fetchReleases:()=>L,fetchRepositories:()=>P,fetchRepository:()=>C,suggestLinkedIdentities:()=>j});module.exports=k(E);var p=class extends Error{status;statusText;constructor(e,n,i){super(i),this.name="GitHubHttpError",this.status=e,this.statusText=n}},c=class extends Error{limit;remaining;resetTime;constructor(e,n,i,r){super(r),this.name="GitHubRateLimitError",this.limit=e,this.remaining=n,this.resetTime=i}};function _(t){let e=t?.token??process.env.GITHUB_TOKEN??process.env.GITHUB_PAT,n=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:n,headers:i}}async function R(t,e){let n=await fetch(t,{headers:e}),i=n.headers.get("x-ratelimit-limit"),r=n.headers.get("x-ratelimit-remaining"),s=n.headers.get("x-ratelimit-reset");if((n.status===403||n.status===429)&&r==="0"&&s){let a=i?Number.parseInt(i,10):0,g=Number.parseInt(r,10),o=new Date(Number.parseInt(s,10)*1e3);throw new c(a,g,o,`GitHub API Rate Limit Exceeded. Resets at ${o.toISOString()}`)}if(!n.ok){let a=await n.text().catch(()=>"");throw new p(n.status,n.statusText,`GitHub API request failed with status ${n.status}: ${a||n.statusText}`)}return n}async function u(t,e){let{baseUrl:n,headers:i}=_(e),r=t.startsWith("http://")||t.startsWith("https://")?t:`${n}${t.startsWith("/")?"":"/"}${t}`;return(await R(r,i)).json()}async function m(t,e){let{baseUrl:n,headers:i}=_(e),r=[],s=null,a=e?.perPage??100,g=e?.page,o=t;if(!o.startsWith("http://")&&!o.startsWith("https://")){let b=o.includes("?")?"&":"?",l=[];a!==void 0&&l.push(`per_page=${a}`),g!==void 0&&l.push(`page=${g}`),l.length>0&&(o=`${o}${b}${l.join("&")}`)}do{let b=s??(o.startsWith("http://")||o.startsWith("https://")?o:`${n}${o.startsWith("/")?"":"/"}${o}`),l=await R(b,i),h=await l.json();if(Array.isArray(h))r.push(...h);else break;if(e?.allPages===!1||g!==void 0)break;let f=l.headers.get("link");if(f){let y=f.match(/<([^>]+)>;\s*rel="next"/);s=y?y[1]:null}else s=null}while(s);return r}function N(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 w(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 $(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 v(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 u(`/users/${t}`,e)).type==="Organization"?"Organization":"User"}catch{return"User"}}async function A(t,e){let n=t?`/users/${t}`:"/user",i=await u(n,e);if(i.type==="Organization")throw new Error(`Requested entity '${t||"authenticated user"}' is an Organization, not a User.`);return N(i)}async function P(t,e){let n;return t?n=await I(t,e)==="Organization"?`/orgs/${t}/repos`:`/users/${t}/repos`:n="/user/repos",(await m(n,e)).map(z)}async function C(t,e,n){let i=await u(`/repos/${t}/${e}`,n);return z(i)}async function T(t,e){let n=t?`/users/${t}/orgs`:"/user/orgs",i=await m(n,e);return(await Promise.all(i.map(s=>u(`/orgs/${s.login}`,e)))).map(w)}async function F(t,e,n){return(await m(`/repos/${t}/${e}/contributors`,n)).map($)}async function D(t,e,n){let i=await u(`/repos/${t}/${e}/languages`,n);return Object.entries(i).map(([r,s])=>({name:r,bytes:s}))}async function L(t,e,n){return(await m(`/repos/${t}/${e}/releases`,n)).map(v)}async function W(t,e){let n=await u(`/orgs/${t}`,e);return w(n)}function B(t){let e=t.trim();try{let n=new URL(e.startsWith("http")?e:`https://${e}`),i=n.hostname.toLowerCase();if(i.includes("github.com")){let r=n.pathname.split("/").filter(Boolean);if(r.length===1)return{platform:"github",type:"unknown",owner:r[0],rawInput:e};if(r.length>=2)return{platform:"github",type:"repo",owner:r[0],repo:r[1],rawInput:e}}if(i.includes("npmjs.com")){let r=n.pathname.split("/").filter(Boolean);if(r[0]==="package")return{platform:"npm",type:"package",name:r.slice(1).join("/"),rawInput:e};if(r[0].startsWith("~"))return{platform:"npm",type:"user",name:r[0].slice(1),rawInput:e};if(r[0].startsWith("@"))return r.length===1?{platform:"npm",type:"org",name:r[0],rawInput:e}:{platform:"npm",type:"package",name:`${r[0]}/${r[1]}`,rawInput:e}}if(i.includes("stackoverflow.com")){let r=n.pathname.split("/").filter(Boolean);if(r[0]==="users"&&r[1])return{platform:"stackoverflow",type:"user",profileId:r[1],name:r[2]||void 0,rawInput:e}}}catch{}if(e.includes("/")){let n=e.split("/");return{platform:"github",type:"repo",owner:n[0],repo:n[1],rawInput:e}}return{platform:"github",type:"unknown",owner:e,rawInput:e}}function j(t,e){let n={},i=[t.blog,t.bio,t.company].filter(Boolean),r=/stackoverflow\.com\/users\/(\d+)\/([a-zA-Z0-9_-]+)?/;for(let s of i){let a=s.match(r);if(a){n.stackoverflow={profileId:a[1],displayName:a[2]||t.login,url:`https://stackoverflow.com/users/${a[1]}/${a[2]||""}`};break}}return n.npm={username:t.login,url:`https://www.npmjs.com/~${t.login}`},n}0&&(module.exports={GitHubHttpError,GitHubRateLimitError,detectInput,fetchContributors,fetchDeveloper,fetchLanguages,fetchOrganization,fetchOrganizations,fetchReleases,fetchRepositories,fetchRepository,suggestLinkedIdentities});
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 c=class extends Error{status;statusText;constructor(n,e,i){super(i),this.name="GitHubHttpError",this.status=n,this.statusText=e}},m=class extends Error{limit;remaining;resetTime;constructor(n,e,i,r){super(r),this.name="GitHubRateLimitError",this.limit=n,this.remaining=e,this.resetTime=i}};function y(t){let n=t?.token??process.env.GITHUB_TOKEN??process.env.GITHUB_PAT,e=t?.baseUrl??"https://api.github.com",i={Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28"};return n&&(i.Authorization=`Bearer ${n}`),{baseUrl:e,headers:i}}async function _(t,n){let e=await fetch(t,{headers:n}),i=e.headers.get("x-ratelimit-limit"),r=e.headers.get("x-ratelimit-remaining"),s=e.headers.get("x-ratelimit-reset");if((e.status===403||e.status===429)&&r==="0"&&s){let a=i?Number.parseInt(i,10):0,g=Number.parseInt(r,10),o=new Date(Number.parseInt(s,10)*1e3);throw new m(a,g,o,`GitHub API Rate Limit Exceeded. Resets at ${o.toISOString()}`)}if(!e.ok){let a=await e.text().catch(()=>"");throw new c(e.status,e.statusText,`GitHub API request failed with status ${e.status}: ${a||e.statusText}`)}return e}async function u(t,n){let{baseUrl:e,headers:i}=y(n),r=t.startsWith("http://")||t.startsWith("https://")?t:`${e}${t.startsWith("/")?"":"/"}${t}`;return(await _(r,i)).json()}async function p(t,n){let{baseUrl:e,headers:i}=y(n),r=[],s=null,a=n?.perPage??100,g=n?.page,o=t;if(!o.startsWith("http://")&&!o.startsWith("https://")){let b=o.includes("?")?"&":"?",l=[];a!==void 0&&l.push(`per_page=${a}`),g!==void 0&&l.push(`page=${g}`),l.length>0&&(o=`${o}${b}${l.join("&")}`)}do{let b=s??(o.startsWith("http://")||o.startsWith("https://")?o:`${e}${o.startsWith("/")?"":"/"}${o}`),l=await _(b,i),d=await l.json();if(Array.isArray(d))r.push(...d);else break;if(n?.allPages===!1||g!==void 0)break;let h=l.headers.get("link");if(h){let f=h.match(/<([^>]+)>;\s*rel="next"/);s=f?f[1]:null}else s=null}while(s);return r}function w(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 R(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 z(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 H(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 G(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 x(t,n){try{return(await u(`/users/${t}`,n)).type==="Organization"?"Organization":"User"}catch{return"User"}}async function $(t,n){let e=t?`/users/${t}`:"/user",i=await u(e,n);if(i.type==="Organization")throw new Error(`Requested entity '${t||"authenticated user"}' is an Organization, not a User.`);return w(i)}async function v(t,n){let e;return t?e=await x(t,n)==="Organization"?`/orgs/${t}/repos`:`/users/${t}/repos`:e="/user/repos",(await p(e,n)).map(R)}async function I(t,n,e){let i=await u(`/repos/${t}/${n}`,e);return R(i)}async function A(t,n){let e=t?`/users/${t}/orgs`:"/user/orgs",i=await p(e,n);return(await Promise.all(i.map(s=>u(`/orgs/${s.login}`,n)))).map(z)}async function P(t,n,e){return(await p(`/repos/${t}/${n}/contributors`,e)).map(H)}async function C(t,n,e){let i=await u(`/repos/${t}/${n}/languages`,e);return Object.entries(i).map(([r,s])=>({name:r,bytes:s}))}async function T(t,n,e){return(await p(`/repos/${t}/${n}/releases`,e)).map(G)}async function F(t,n){let e=await u(`/orgs/${t}`,n);return z(e)}function D(t){let n=t.trim();try{let e=new URL(n.startsWith("http")?n:`https://${n}`),i=e.hostname.toLowerCase();if(i.includes("github.com")){let r=e.pathname.split("/").filter(Boolean);if(r.length===1)return{platform:"github",type:"unknown",owner:r[0],rawInput:n};if(r.length>=2)return{platform:"github",type:"repo",owner:r[0],repo:r[1],rawInput:n}}if(i.includes("npmjs.com")){let r=e.pathname.split("/").filter(Boolean);if(r[0]==="package")return{platform:"npm",type:"package",name:r.slice(1).join("/"),rawInput:n};if(r[0].startsWith("~"))return{platform:"npm",type:"user",name:r[0].slice(1),rawInput:n};if(r[0].startsWith("@"))return r.length===1?{platform:"npm",type:"org",name:r[0],rawInput:n}:{platform:"npm",type:"package",name:`${r[0]}/${r[1]}`,rawInput:n}}if(i.includes("stackoverflow.com")){let r=e.pathname.split("/").filter(Boolean);if(r[0]==="users"&&r[1])return{platform:"stackoverflow",type:"user",profileId:r[1],name:r[2]||void 0,rawInput:n}}}catch{}if(n.includes("/")){let e=n.split("/");return{platform:"github",type:"repo",owner:e[0],repo:e[1],rawInput:n}}return{platform:"github",type:"unknown",owner:n,rawInput:n}}function L(t,n){let e={},i=[t.blog,t.bio,t.company].filter(Boolean),r=/stackoverflow\.com\/users\/(\d+)\/([a-zA-Z0-9_-]+)?/;for(let s of i){let a=s.match(r);if(a){e.stackoverflow={profileId:a[1],displayName:a[2]||t.login,url:`https://stackoverflow.com/users/${a[1]}/${a[2]||""}`};break}}return e.npm={username:t.login,url:`https://www.npmjs.com/~${t.login}`},e}export{c as GitHubHttpError,m as GitHubRateLimitError,D as detectInput,P as fetchContributors,$ as fetchDeveloper,C as fetchLanguages,F as fetchOrganization,A as fetchOrganizations,T as fetchReleases,v as fetchRepositories,I as fetchRepository,L as suggestLinkedIdentities};
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.0.0",
6
- "description": "Typed utilities for fetching and normalizing GitHub REST and GraphQL data into a consistent domain model for analytics and reporting.",
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": "latest"
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
- "@ossintel/github-normalizer",
57
- "turboforge"
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",