@ossintel/scoring 0.0.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 turboforge-dev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # @ossintel/scoring <img src="https://raw.githubusercontent.com/mayank1513/mayank1513/main/popper.png" style="height: 40px"/>
2
+
3
+ <p className="flex gap-2">
4
+ <a href="https://github.com/mayank1513/ossintel/actions/workflows/ci.yml" rel="noopener noreferrer">
5
+ <img alt="CI" src="https://github.com/mayank1513/ossintel/actions/workflows/ci.yml/badge.svg" />
6
+ </a>
7
+ <a href="https://codecov.io/gh/mayank1513/ossintel/tree/main/packages/@ossintel/scoring" rel="noopener noreferrer">
8
+ <img alt="codecov" src="https://codecov.io/gh/mayank1513/ossintel/graph/badge.svg?flag=@ossintel/scoring" />
9
+ </a>
10
+ <a href="https://npmjs.com/package/@ossintel/scoring" rel="noopener noreferrer">
11
+ <img alt="npm version" src="https://img.shields.io/npm/v/@ossintel/scoring" />
12
+ </a>
13
+ <a href="https://npmjs.com/package/@ossintel/scoring" rel="noopener noreferrer">
14
+ <img alt="npm downloads" src="https://img.shields.io/npm/d18m/@ossintel/scoring" />
15
+ </a>
16
+ <a href="https://npmjs.com/package/@ossintel/scoring" rel="noopener noreferrer">
17
+ <img alt="npm bundle size" src="https://img.shields.io/bundlephobia/minzip/@ossintel/scoring" />
18
+ </a>
19
+ <img alt="license" src="https://img.shields.io/npm/l/@ossintel/scoring" />
20
+ </p>
21
+
22
+ > @ossintel/scoring: Deterministic scoring engine for calculating OSS health, impact, risk, activity, and community metrics from normalized repository and ecosystem data.
23
+
24
+ # @ossintel/scoring
25
+
26
+ Deterministic OSS scoring engine.
27
+
28
+ ## Purpose
29
+
30
+ Converts normalized repository and developer metrics into objective scores.
31
+
32
+ ## Responsibilities
33
+
34
+ Calculate:
35
+
36
+ - Overall Score
37
+ - Health Score
38
+ - Impact Score
39
+ - Activity Score
40
+ - Community Score
41
+ - Risk Score
42
+
43
+ ## Philosophy
44
+
45
+ Scores should always be reproducible.
46
+
47
+ The same inputs should always produce the same outputs.
48
+
49
+ No AI should participate in score calculation.
50
+
51
+ ## Inputs
52
+
53
+ Normalized domain models.
54
+
55
+ ## Outputs
56
+
57
+ ```ts
58
+ {
59
+ overall, health, impact, activity, community, risk;
60
+ }
61
+ ```
62
+
63
+ ## Non-goals
64
+
65
+ - API calls
66
+ - AI
67
+ - Charts
68
+ - UI
69
+
70
+ ## Testing
71
+
72
+ Every scoring algorithm should have comprehensive unit tests.
73
+
74
+ No external dependencies should affect score calculation.
75
+
76
+ ---
77
+
78
+ ## ✨ Why @ossintel/scoring?
79
+
80
+ -
81
+ -
82
+
83
+ ---
84
+
85
+ ## 📦 Installation
86
+
87
+ ```bash
88
+ $ pnpm add @ossintel/scoring
89
+ ```
90
+
91
+ **_or_**
92
+
93
+ ```bash
94
+ $ npm install @ossintel/scoring
95
+ ```
96
+
97
+ **_or_**
98
+
99
+ ```bash
100
+ $ yarn add @ossintel/scoring
101
+ ```
102
+
103
+ ## License
104
+
105
+ This library is licensed under the MIT open-source license.
106
+
107
+ <hr />
108
+
109
+ <p align="center">with 💖 by <a href="https://mayankchaudhari.com" target="_blank">Mayank Kumar Chaudhari</a></p>
@@ -0,0 +1,36 @@
1
+ import { NormalizedRepository, NormalizedContributor, NormalizedRelease, NormalizedLanguage } from '@ossintel/github-normalizer';
2
+
3
+ interface ScoringInputs {
4
+ repository: NormalizedRepository;
5
+ contributors?: NormalizedContributor[];
6
+ releases?: NormalizedRelease[];
7
+ languages?: NormalizedLanguage[];
8
+ }
9
+ interface RepositoryScores {
10
+ overall: number;
11
+ health: number;
12
+ impact: number;
13
+ activity: number;
14
+ community: number;
15
+ risk: number;
16
+ }
17
+ interface NpmPackageStats {
18
+ name: string;
19
+ downloads: number;
20
+ stars?: number;
21
+ }
22
+ interface IdentityScoringInputs {
23
+ repositories: NormalizedRepository[];
24
+ npmPackages?: NpmPackageStats[];
25
+ }
26
+ type IdentityScores = RepositoryScores;
27
+
28
+ declare const calculateImpactScore: (repository: ScoringInputs["repository"]) => number;
29
+ declare const calculateActivityScore: (repository: ScoringInputs["repository"], releases: ScoringInputs["releases"]) => number;
30
+ declare const calculateCommunityScore: (repository: ScoringInputs["repository"], contributors: ScoringInputs["contributors"]) => number;
31
+ declare const calculateHealthScore: (repository: ScoringInputs["repository"]) => number;
32
+ declare const calculateRiskScore: (repository: ScoringInputs["repository"], contributors: ScoringInputs["contributors"]) => number;
33
+ declare const calculateRepositoryScore: (inputs: ScoringInputs) => RepositoryScores;
34
+ declare const calculateIdentityScore: (inputs: IdentityScoringInputs) => IdentityScores;
35
+
36
+ export { type IdentityScores, type IdentityScoringInputs, type NpmPackageStats, type RepositoryScores, type ScoringInputs, calculateActivityScore, calculateCommunityScore, calculateHealthScore, calculateIdentityScore, calculateImpactScore, calculateRepositoryScore, calculateRiskScore };
@@ -0,0 +1,36 @@
1
+ import { NormalizedRepository, NormalizedContributor, NormalizedRelease, NormalizedLanguage } from '@ossintel/github-normalizer';
2
+
3
+ interface ScoringInputs {
4
+ repository: NormalizedRepository;
5
+ contributors?: NormalizedContributor[];
6
+ releases?: NormalizedRelease[];
7
+ languages?: NormalizedLanguage[];
8
+ }
9
+ interface RepositoryScores {
10
+ overall: number;
11
+ health: number;
12
+ impact: number;
13
+ activity: number;
14
+ community: number;
15
+ risk: number;
16
+ }
17
+ interface NpmPackageStats {
18
+ name: string;
19
+ downloads: number;
20
+ stars?: number;
21
+ }
22
+ interface IdentityScoringInputs {
23
+ repositories: NormalizedRepository[];
24
+ npmPackages?: NpmPackageStats[];
25
+ }
26
+ type IdentityScores = RepositoryScores;
27
+
28
+ declare const calculateImpactScore: (repository: ScoringInputs["repository"]) => number;
29
+ declare const calculateActivityScore: (repository: ScoringInputs["repository"], releases: ScoringInputs["releases"]) => number;
30
+ declare const calculateCommunityScore: (repository: ScoringInputs["repository"], contributors: ScoringInputs["contributors"]) => number;
31
+ declare const calculateHealthScore: (repository: ScoringInputs["repository"]) => number;
32
+ declare const calculateRiskScore: (repository: ScoringInputs["repository"], contributors: ScoringInputs["contributors"]) => number;
33
+ declare const calculateRepositoryScore: (inputs: ScoringInputs) => RepositoryScores;
34
+ declare const calculateIdentityScore: (inputs: IdentityScoringInputs) => IdentityScores;
35
+
36
+ export { type IdentityScores, type IdentityScoringInputs, type NpmPackageStats, type RepositoryScores, type ScoringInputs, calculateActivityScore, calculateCommunityScore, calculateHealthScore, calculateIdentityScore, calculateImpactScore, calculateRepositoryScore, calculateRiskScore };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var M=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var R=Object.prototype.hasOwnProperty;var F=(t,e)=>{for(var o in e)M(t,o,{get:e[o],enumerable:!0})},z=(t,e,o,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let c of A(e))!R.call(t,c)&&c!==o&&M(t,c,{get:()=>e[c],enumerable:!(i=x(e,c))||i.enumerable});return t};var T=t=>z(M({},"__esModule",{value:!0}),t);var H={};F(H,{calculateActivityScore:()=>C,calculateCommunityScore:()=>w,calculateHealthScore:()=>y,calculateIdentityScore:()=>Y,calculateImpactScore:()=>k,calculateRepositoryScore:()=>v,calculateRiskScore:()=>D});module.exports=T(H);var k=t=>{let e=t.stargazersCount,o=t.forksCount,i=t.watchersCount,c=Math.min(100,Math.log10(e+1)*20),n=Math.min(100,Math.log10(o+1)*25),s=Math.min(100,Math.log10(i+1)*30);return Math.round(c*.5+n*.35+s*.15)},C=(t,e)=>{if(t.isArchived)return 0;let o=new Date,i=new Date(t.pushedAt),c=o.getTime()-i.getTime(),n=Math.max(0,c/(1e3*60*60*24)),s=0;n<=1?s=100:n<=7?s=90:n<=30?s=80:n<=90?s=60:n<=180?s=40:n<=365&&(s=20);let u=50;if(e){let l=new Date;l.setFullYear(o.getFullYear()-1);let p=e.filter(f=>f.publishedAt&&new Date(f.publishedAt)>=l).length;u=Math.min(100,p*20)}return Math.round(s*.6+u*.4)},w=(t,e)=>{let o=e?e.length:0,i=Math.min(100,Math.log10(o+1)*50),c=t.topics.length>0?100:0,n=(t.description?50:0)+(t.homepage?50:0);return Math.round(i*.7+c*.15+n*.15)},y=t=>{if(t.isArchived)return 0;let e=t.stargazersCount+t.forksCount,o=100;if(t.openIssuesCount>0){let l=t.openIssuesCount/(e+10);o=Math.max(0,100-l*500)}let i=new Date(t.updatedAt),c=Date.now()-i.getTime(),n=Math.max(0,c/(1e3*60*60*24)),s=0;n<=30?s=100:n<=90?s=80:n<=180?s=60:n<=365?s=40:s=10;let u=t.isFork?50:100;return Math.round(o*.5+s*.3+u*.2)},D=(t,e)=>{let o=0,i=new Date(t.pushedAt),c=(Date.now()-i.getTime())/(1e3*60*60*24);c>365?o+=30:c>180?o+=20:c>90&&(o+=10);let n=e?e.length:1;n<=1?o+=30:n<=3?o+=20:n<=5&&(o+=10),t.isFork&&(o+=20);let s=t.stargazersCount+t.forksCount;return t.openIssuesCount>50&&t.openIssuesCount>s?o+=20:t.openIssuesCount>20&&(o+=10),Math.min(100,Math.round(o))},v=t=>{let{repository:e,contributors:o,releases:i}=t,c=y(e),n=k(e),s=C(e,i),u=w(e,o),l=D(e,o);return{overall:Math.round(c*.3+n*.25+s*.2+u*.15+(100-l)*.1),health:c,impact:n,activity:s,community:u,risk:l}},Y=t=>{let{repositories:e,npmPackages:o}=t;if(e.length===0)return{overall:0,health:0,impact:0,activity:0,community:0,risk:100};let i=e.reduce((r,a)=>r+a.stargazersCount,0),c=e.reduce((r,a)=>r+a.forksCount,0),n=e.reduce((r,a)=>r+a.watchersCount,0),s=Math.min(100,Math.log10(i+1)*20),u=Math.min(100,Math.log10(c+1)*25),l=Math.min(100,Math.log10(n+1)*30),p=s*.5+u*.35+l*.15;if(o&&o.length>0){let r=o.reduce((h,b)=>h+b.downloads,0),a=Math.min(100,Math.log10(r+1)*15);p=p*.8+a*.2}let f=Math.round(p),I=e.filter(r=>!r.isArchived),m=0,d=0,g=0,S=100;if(I.length>0){let r=I.map(a=>v({repository:a}));m=Math.round(r.reduce((a,h)=>a+h.health,0)/r.length),d=Math.round(r.reduce((a,h)=>a+h.activity,0)/r.length),g=Math.round(r.reduce((a,h)=>a+h.community,0)/r.length),S=Math.round(r.reduce((a,h)=>a+h.risk,0)/r.length)}return{overall:Math.round(m*.3+f*.25+d*.2+g*.15+(100-S)*.1),health:m,impact:f,activity:d,community:g,risk:S}};0&&(module.exports={calculateActivityScore,calculateCommunityScore,calculateHealthScore,calculateIdentityScore,calculateImpactScore,calculateRepositoryScore,calculateRiskScore});
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ var k=t=>{let o=t.stargazersCount,e=t.forksCount,i=t.watchersCount,a=Math.min(100,Math.log10(o+1)*20),n=Math.min(100,Math.log10(e+1)*25),s=Math.min(100,Math.log10(i+1)*30);return Math.round(a*.5+n*.35+s*.15)},C=(t,o)=>{if(t.isArchived)return 0;let e=new Date,i=new Date(t.pushedAt),a=e.getTime()-i.getTime(),n=Math.max(0,a/(1e3*60*60*24)),s=0;n<=1?s=100:n<=7?s=90:n<=30?s=80:n<=90?s=60:n<=180?s=40:n<=365&&(s=20);let u=50;if(o){let l=new Date;l.setFullYear(e.getFullYear()-1);let p=o.filter(f=>f.publishedAt&&new Date(f.publishedAt)>=l).length;u=Math.min(100,p*20)}return Math.round(s*.6+u*.4)},w=(t,o)=>{let e=o?o.length:0,i=Math.min(100,Math.log10(e+1)*50),a=t.topics.length>0?100:0,n=(t.description?50:0)+(t.homepage?50:0);return Math.round(i*.7+a*.15+n*.15)},y=t=>{if(t.isArchived)return 0;let o=t.stargazersCount+t.forksCount,e=100;if(t.openIssuesCount>0){let l=t.openIssuesCount/(o+10);e=Math.max(0,100-l*500)}let i=new Date(t.updatedAt),a=Date.now()-i.getTime(),n=Math.max(0,a/(1e3*60*60*24)),s=0;n<=30?s=100:n<=90?s=80:n<=180?s=60:n<=365?s=40:s=10;let u=t.isFork?50:100;return Math.round(e*.5+s*.3+u*.2)},D=(t,o)=>{let e=0,i=new Date(t.pushedAt),a=(Date.now()-i.getTime())/(1e3*60*60*24);a>365?e+=30:a>180?e+=20:a>90&&(e+=10);let n=o?o.length:1;n<=1?e+=30:n<=3?e+=20:n<=5&&(e+=10),t.isFork&&(e+=20);let s=t.stargazersCount+t.forksCount;return t.openIssuesCount>50&&t.openIssuesCount>s?e+=20:t.openIssuesCount>20&&(e+=10),Math.min(100,Math.round(e))},v=t=>{let{repository:o,contributors:e,releases:i}=t,a=y(o),n=k(o),s=C(o,i),u=w(o,e),l=D(o,e);return{overall:Math.round(a*.3+n*.25+s*.2+u*.15+(100-l)*.1),health:a,impact:n,activity:s,community:u,risk:l}},x=t=>{let{repositories:o,npmPackages:e}=t;if(o.length===0)return{overall:0,health:0,impact:0,activity:0,community:0,risk:100};let i=o.reduce((c,r)=>c+r.stargazersCount,0),a=o.reduce((c,r)=>c+r.forksCount,0),n=o.reduce((c,r)=>c+r.watchersCount,0),s=Math.min(100,Math.log10(i+1)*20),u=Math.min(100,Math.log10(a+1)*25),l=Math.min(100,Math.log10(n+1)*30),p=s*.5+u*.35+l*.15;if(e&&e.length>0){let c=e.reduce((h,I)=>h+I.downloads,0),r=Math.min(100,Math.log10(c+1)*15);p=p*.8+r*.2}let f=Math.round(p),M=o.filter(c=>!c.isArchived),m=0,d=0,g=0,S=100;if(M.length>0){let c=M.map(r=>v({repository:r}));m=Math.round(c.reduce((r,h)=>r+h.health,0)/c.length),d=Math.round(c.reduce((r,h)=>r+h.activity,0)/c.length),g=Math.round(c.reduce((r,h)=>r+h.community,0)/c.length),S=Math.round(c.reduce((r,h)=>r+h.risk,0)/c.length)}return{overall:Math.round(m*.3+f*.25+d*.2+g*.15+(100-S)*.1),health:m,impact:f,activity:d,community:g,risk:S}};export{C as calculateActivityScore,w as calculateCommunityScore,y as calculateHealthScore,x as calculateIdentityScore,k as calculateImpactScore,v as calculateRepositoryScore,D as calculateRiskScore};
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@ossintel/scoring",
3
+ "author": "Mayank Kumar Chaudhari <https://mayankchaudhari.com>",
4
+ "private": false,
5
+ "version": "0.0.0",
6
+ "description": "Deterministic scoring engine for calculating OSS health, impact, risk, activity, and community metrics from normalized repository and ecosystem data.",
7
+ "license": "MIT",
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.mjs",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": {
14
+ "types": "./dist/index.d.mts",
15
+ "default": "./dist/index.mjs"
16
+ },
17
+ "require": {
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ }
21
+ },
22
+ "./package.json": "./package.json"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/mayank1513/ossintel",
27
+ "directory": "packages/scoring"
28
+ },
29
+ "bugs": "https://github.com/mayank1513/ossintel/issues",
30
+ "homepage": "https://github.com/mayank1513/ossintel/blob/main/packages/scoring/README.md",
31
+ "sideEffects": false,
32
+ "files": [
33
+ "dist/**"
34
+ ],
35
+ "dependencies": {
36
+ "@ossintel/github-normalizer": "0.0.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "latest",
40
+ "tsup": "latest",
41
+ "typescript": "latest"
42
+ },
43
+ "forge": {
44
+ "aliases": [],
45
+ "icon": "LibraryBig",
46
+ "description": ""
47
+ },
48
+ "funding": [
49
+ {
50
+ "type": "github",
51
+ "url": "https://github.com/sponsors/mayank1513"
52
+ }
53
+ ],
54
+ "keywords": [
55
+ "@ossintel/scoring",
56
+ "turboforge"
57
+ ],
58
+ "scripts": {
59
+ "build": "tsup && gzip -c dist/index.js | wc -c",
60
+ "clean": "rm -rf dist",
61
+ "dev": "tsup --watch"
62
+ }
63
+ }