@unveil/vk 1.0.0-beta.0 → 1.0.0-beta.1

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/README.md CHANGED
@@ -33,11 +33,18 @@ console.log(eventsCount); // how many events we actually got back
33
33
 
34
34
  ```js
35
35
  analyze(username, {
36
- token: "ghp_...", // GitHub token. Optional, but strongly recommended
36
+ provider: "gitlab", // "github" (default), "gitlab", "codeberg" or "tangled"
37
+ token: "ghp_...", // provider token. Optional, but strongly recommended
37
38
  showEvents: true, // return the raw events too. Off by default
38
39
  });
39
40
  ```
40
41
 
42
+ `provider` picks where the account lives: `github` (api.github.com),
43
+ `gitlab` (gitlab.com), `codeberg` (codeberg.org) or `tangled` (resolved through
44
+ AT Protocol). Leave it out and it is GitHub. GitLab.com only returns a user's
45
+ sign-up date to authenticated requests, so `gitlab` needs a token. Tangled
46
+ records are public and no token is sent.
47
+
41
48
  `token` is sent as `Authorization: Bearer`. Without it you are on the unauthenticated
42
49
  rate limit, which is 60 requests per hour for the whole IP — and each `analyze`
43
50
  call spends 4 of them. With a token you get 5000 per hour. The token needs no
@@ -75,7 +82,7 @@ analysis reports lower `confidence` to match.
75
82
 
76
83
  `analyze` rejects instead of returning a partial result. A missing user, a
77
84
  revoked token or an exhausted rate limit all come back as a plain `Error`
78
- carrying GitHub's own message, with the `status` and `url` of the failed request
85
+ carrying the provider's own message, with the `status` and `url` of the failed request
79
86
  attached to it:
80
87
 
81
88
  ```js
package/dist/index.d.mts CHANGED
@@ -1,21 +1,29 @@
1
1
  import { IdentifyResult } from "@unveil/identity";
2
+ import { CodebergActivity } from "@unveil/identity/providers/codeberg";
2
3
  import { GitHubEvent } from "@unveil/identity/providers/github";
4
+ import { GitLabEvent } from "@unveil/identity/providers/gitlab";
5
+ import { TangledRecord } from "@unveil/identity/providers/tangled";
6
+ //#region src/http.d.ts
7
+ declare class ProviderError extends Error {
8
+ readonly status: number;
9
+ readonly url: string;
10
+ constructor(message: string, status: number, url: string);
11
+ }
12
+ //#endregion
3
13
  //#region src/index.d.ts
14
+ type Provider = "github" | "gitlab" | "codeberg" | "tangled";
15
+ type ProviderEvent = GitHubEvent | GitLabEvent | CodebergActivity | TangledRecord;
4
16
  type AnalyzeOptions = {
5
17
  token?: string;
6
18
  showEvents?: boolean;
19
+ provider?: Provider;
7
20
  };
8
21
  type AnalyzeReturn = {
9
22
  analysis: IdentifyResult;
10
- events: GitHubEvent[];
23
+ events: ProviderEvent[];
11
24
  eventsCount: number;
12
- userId?: number;
25
+ userId?: number | string;
13
26
  };
14
- declare class GitHubError extends Error {
15
- readonly status: number;
16
- readonly url: string;
17
- constructor(message: string, status: number, url: string);
18
- }
19
27
  declare function analyze(username: string, options?: AnalyzeOptions): Promise<AnalyzeReturn>;
20
28
  //#endregion
21
- export { AnalyzeOptions, AnalyzeReturn, GitHubError, type GitHubEvent, type IdentifyResult, analyze };
29
+ export { AnalyzeOptions, AnalyzeReturn, type CodebergActivity, type GitHubEvent, type GitLabEvent, type IdentifyResult, Provider, ProviderError, ProviderEvent, type TangledRecord, analyze };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{identify as e}from"@unveil/identity";import{fromGitHub as t}from"@unveil/identity/providers/github";var n=class extends Error{status;url;constructor(e,t,n){super(e),this.name=`GitHubError`,this.status=t,this.url=n}};async function r(e){try{let t=await e.json();if(t?.message)return t.message}catch{}return`${e.status} ${e.statusText}`}async function i(e,t){let i=`https://api.github.com${e}`,a={Accept:`application/vnd.github+json`,"X-GitHub-Api-Version":`2022-11-28`,"User-Agent":`unveil-vk`};t&&(a.Authorization=`Bearer ${t}`);let o=await fetch(i,{headers:a});if(!o.ok)throw new n(await r(o),o.status,i);return await o.json()}async function a(n,r){let a=r?.token,o=encodeURIComponent(n),s=await i(`/users/${o}`,a),c=Array.from({length:3},(e,t)=>i(`/users/${o}/events/public?per_page=100&page=${t+1}`,a)),l=(await Promise.all(c)).flat();return{analysis:e(t({user:s,events:l})),userId:s.id,events:r?.showEvents?l:[],eventsCount:l.length}}export{n as GitHubError,a as analyze};
1
+ import{identify as e}from"@unveil/identity";import{fromCodeberg as t}from"@unveil/identity/providers/codeberg";import{fromGitHub as n}from"@unveil/identity/providers/github";import{fromGitLab as r}from"@unveil/identity/providers/gitlab";import{fromTangled as i}from"@unveil/identity/providers/tangled";function a(e,t){let n=new URLSearchParams;for(let[e,r]of Object.entries(t))n.set(e,String(r));return`${e}?${n}`}var o=class extends Error{status;url;constructor(e,t,n){super(e),this.name=`ProviderError`,this.status=t,this.url=n}};async function s(e){try{let t=await e.json();if(t?.message)return t.message}catch{}return`${e.status} ${e.statusText}`}async function c(e,t={}){let n=await fetch(e,{headers:t});if(!n.ok)throw new o(await s(n),n.status,e);return n}async function l(e,t){return await(await c(e,t)).json()}async function u(e,t,n){let r=Array.from({length:e},(e,r)=>l(t(r+1),n));return(await Promise.all(r)).flat()}function d(e,t={}){return e?{...t,Authorization:`Bearer ${e}`}:t}async function f(e,n){let r=encodeURIComponent(e),i=d(n),o=`https://codeberg.org/api/v1/users/${r}`,s=e=>a(`${o}/activities/feeds`,{"only-performed-by":!0,limit:50,page:e}),f=a(`${o}/repos`,{limit:1}),[p,m,h]=await Promise.all([l(o,i),u(6,s,i),c(f,i)]);return{input:t({user:p,events:m,publicRepos:Number(h.headers.get(`X-Total-Count`)??0)}),events:m,userId:p.id}}const p=`https://api.github.com`;async function m(e,t){let r=encodeURIComponent(e),i=d(t,{Accept:`application/vnd.github+json`,"X-GitHub-Api-Version":`2022-11-28`,"User-Agent":`unveil-vk`}),o=await l(`${p}/users/${r}`,i),s=await u(3,e=>a(`${p}/users/${r}/events/public`,{per_page:100,page:e}),i);return{input:n({user:o,events:s}),events:s,userId:o.id}}const h=`https://gitlab.com/api/v4`;function g(e){let t=new Set;for(let n of e)n.project_id&&t.add(n.project_id);return[...t]}async function _(e,t){try{return await l(`${h}/projects/${e}`,t)}catch(e){if(e instanceof o&&e.status===404)return;throw e}}async function v(e,t){let n=d(t),i=a(`${h}/users`,{username:e}),[s]=await l(i,n);if(!s)throw new o(`404 User Not Found`,404,i);let f=`${h}/users/${s.id}`,p=e=>a(`${f}/events`,{per_page:100,page:e}),m=a(`${f}/projects`,{per_page:1}),[v,y,b]=await Promise.all([l(f,n),u(3,p,n),c(m,n)]),x=g(y),S=await Promise.all(x.map(e=>_(e,n)));return{input:r({user:v,events:y,publicRepos:Number(b.headers.get(`X-Total`)??0),projects:S.filter(e=>e!==void 0)}),events:y,userId:v.id}}const y=`https://plc.directory`;function b(e){return e.startsWith(`did:plc:`)}async function x(e){return e.startsWith(`did:`)?e:(await l(a(`https://bsky.social/xrpc/com.atproto.identity.resolveHandle`,{handle:e}))).did}function S(e){return b(e)?`${y}/${e}`:`https://${e.replace(/^did:web:/,``)}/.well-known/did.json`}function C(e){return e.service?.find(e=>e.id.endsWith(`#atproto_pds`))?.serviceEndpoint}function w(e){return(e.alsoKnownAs?.[0])?.replace(/^at:\/\//,``)}async function T(e){return(await l(`${y}/${e}/log/audit`))[0]?.createdAt??``}async function E(e,t){return(await l(a(`${t}/xrpc/com.atproto.repo.getRecord`,{repo:e,collection:`app.bsky.actor.profile`,rkey:`self`}))).value.createdAt??``}async function D(e,t){return b(e)?T(e):E(e,t)}async function O(e,t,n,r){return(await l(a(`${t}/xrpc/com.atproto.repo.listRecords`,{repo:e,collection:n,limit:r}))).records}const k=[`repo`,`actor.profile`].map(e=>`sh.tangled.${e}`),A=[`repo.issue`,`repo.issue.state`,`repo.issue.comment`,`repo.pull`,`repo.pull.status`,`repo.pull.comment`,`feed.comment`,`feed.star`,`feed.reaction`,`graph.follow`,`repo.collaborator`,`string`].map(e=>`sh.tangled.${e}`);async function j(e,t,n){return(await Promise.all(n.map(n=>O(e,t,n,100)))).flat()}function M(e,t){return e.toSorted((e,t)=>{let n=e.value.createdAt??``;return(t.value.createdAt??``).localeCompare(n)}).slice(0,t)}async function N(e,t){let[n,r]=await Promise.all([j(e,t,k),j(e,t,A)]);return[...n,...M(r,300)]}async function P(e){let t=await x(e),n=S(t),r=await l(n),a=C(r);if(!a)throw new o(`No PDS found for this account`,404,n);let s=w(r)??e,[c,u]=await Promise.all([D(t,a),N(t,a)]);return{input:i({account:{did:t,handle:s,createdAt:c},records:u}),events:u,userId:t}}const F={github:m,gitlab:v,codeberg:f,tangled:P};async function I(t,n){let r=n?.provider??`github`,i=F[r],{input:a,events:o,userId:s}=await i(t,n?.token);return{analysis:e(a),userId:s,events:n?.showEvents?o:[],eventsCount:o.length}}export{o as ProviderError,I as analyze};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unveil/vk",
3
- "version": "1.0.0-beta.0",
3
+ "version": "1.0.0-beta.1",
4
4
  "description": "Fetch GitHub account activity and analyze it for automation patterns",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.22.0",
@@ -37,13 +37,13 @@
37
37
  "devDependencies": {
38
38
  "@biomejs/biome": "^2.5.6",
39
39
  "@types/node": "^26.1.2",
40
- "@unveil/identity": "3.0.0-beta.1",
40
+ "@unveil/identity": "3.0.0-beta.2",
41
41
  "publint": "^0.3.22",
42
42
  "tsdown": "^0.22.14",
43
43
  "typescript": "^6.0.3",
44
44
  "vitest": "^4.1.10"
45
45
  },
46
46
  "peerDependencies": {
47
- "@unveil/identity": "^3.0.0-beta.1"
47
+ "@unveil/identity": "^3.0.0-beta.2"
48
48
  }
49
49
  }