kyd-shared-badge 0.2.21 → 0.2.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kyd-shared-badge",
3
- "version": "0.2.21",
3
+ "version": "0.2.23",
4
4
  "private": false,
5
5
  "main": "./src/index.ts",
6
6
  "module": "./src/index.ts",
@@ -98,12 +98,12 @@ const ScoreCard = ({ title, score, description, descriptor, scoreType, iconImage
98
98
  };
99
99
 
100
100
  const SharedBadgeDisplay = ({ badgeData }: { badgeData: PublicBadgeData }) => {
101
- const { badgeId, developerName, assessmentResult, updatedAt, connectedPlatforms } = badgeData;
102
- const { summary_scores, report_summary, developer_trust_explanation, key_skills, screening_sources, industry_considerations } = assessmentResult;
101
+ const { badgeId, developerName, assessmentResult, updatedAt, connectedAccounts } = badgeData;
102
+ const { summary_scores, report_summary, developer_trust_explanation, key_skills, screening_sources, industry_considerations, top_contributing_rules } = assessmentResult;
103
103
 
104
- const devTrustScore = summary_scores.developer_trust;
105
- const riskScore = summary_scores.risk_score;
106
- const aiUsageScore = summary_scores.ai_usage;
104
+ const devTrustScore = summary_scores?.developer_trust;
105
+ const riskScore = summary_scores?.risk_score;
106
+ const aiUsageScore = summary_scores?.ai_usage;
107
107
 
108
108
  const wrapperMaxWidth = 'max-w-5xl';
109
109
 
@@ -114,7 +114,7 @@ const SharedBadgeDisplay = ({ badgeData }: { badgeData: PublicBadgeData }) => {
114
114
  badgeId={badgeId}
115
115
  developerName={badgeData.developerName}
116
116
  updatedAt={updatedAt}
117
- score={summary_scores.kyd_self_check.score}
117
+ score={summary_scores?.kyd_self_check?.score}
118
118
  isPublic={true}
119
119
  badgeImageUrl={badgeData.badgeImageUrl || ''}
120
120
  />
@@ -122,30 +122,32 @@ const SharedBadgeDisplay = ({ badgeData }: { badgeData: PublicBadgeData }) => {
122
122
  className={'rounded-xl shadow-xl p-6 sm:p-8 mt-8 border'}
123
123
  style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)' }}
124
124
  >
125
- <div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12">
126
- <ScoreCard
127
- title="KYD Technical™"
128
- score={devTrustScore?.score || 0}
129
- description={devTrustScore?.description || ''}
130
- scoreType='number'
131
- iconImageSrc={getTechnicalIconSrc(devTrustScore?.score || 0)}
132
- />
133
- <ScoreCard
134
- title="KYD Risk™"
135
- score={riskScore?.score || 0}
136
- description={riskScore?.description || ''}
137
- scoreType='risk'
138
- iconImageSrc={getRiskIconSrc(riskScore?.score || 0)}
139
- />
140
- <ScoreCard
141
- title={<span>KYD AI™ <span className="text-xs font-semibold bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 px-2 py-1 rounded-full align-middle">Beta</span></span>}
142
- score={aiUsageScore?.score || 0}
143
- description={aiUsageScore?.description || ''}
144
- descriptor={aiUsageScore?.descriptor}
145
- scoreType='descriptor'
146
- iconImageSrc={getAiIconSrc(aiUsageScore?.descriptor)}
147
- />
148
- </div>
125
+ {summary_scores && (
126
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12">
127
+ <ScoreCard
128
+ title="KYD Technical™"
129
+ score={devTrustScore?.score || 0}
130
+ description={devTrustScore?.description || ''}
131
+ scoreType='number'
132
+ iconImageSrc={getTechnicalIconSrc(devTrustScore?.score || 0)}
133
+ />
134
+ <ScoreCard
135
+ title="KYD Risk™"
136
+ score={riskScore?.score || 0}
137
+ description={riskScore?.description || ''}
138
+ scoreType='risk'
139
+ iconImageSrc={getRiskIconSrc(riskScore?.score || 0)}
140
+ />
141
+ <ScoreCard
142
+ title={<span>KYD AI™ <span className="text-xs font-semibold bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 px-2 py-1 rounded-full align-middle">Beta</span></span>}
143
+ score={aiUsageScore?.score || 0}
144
+ description={aiUsageScore?.description || ''}
145
+ descriptor={aiUsageScore?.descriptor}
146
+ scoreType='descriptor'
147
+ iconImageSrc={getAiIconSrc(aiUsageScore?.descriptor)}
148
+ />
149
+ </div>
150
+ )}
149
151
 
150
152
  <div className={'space-y-12 divide-y'} style={{ borderColor: 'var(--icon-button-secondary)' }}>
151
153
  <div className="pt-8 first:pt-0">
@@ -153,6 +155,16 @@ const SharedBadgeDisplay = ({ badgeData }: { badgeData: PublicBadgeData }) => {
153
155
  <div className={'prose prose-sm max-w-none'} style={{ color: 'var(--text-secondary)' }}>
154
156
  <p>{report_summary}</p>
155
157
  </div>
158
+ {top_contributing_rules && top_contributing_rules.length > 0 && (
159
+ <div className="mt-6">
160
+ <h4 className={'text-lg font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Top Contributing Rules</h4>
161
+ <ul className="list-disc list-inside" style={{ color: 'var(--text-secondary)' }}>
162
+ {top_contributing_rules.map((r, i) => (
163
+ <li key={i}><strong style={{ color: 'var(--text-main)' }}>{r.provider}:</strong> {r.rule_label} — {r.why}</li>
164
+ ))}
165
+ </ul>
166
+ </div>
167
+ )}
156
168
  {key_skills && key_skills.length > 0 && (
157
169
  <div className="mt-6">
158
170
  <h4 className={'text-lg font-semibold mb-3'} style={{ color: 'var(--text-main)' }}>Key Skills Observed</h4>
@@ -173,7 +185,7 @@ const SharedBadgeDisplay = ({ badgeData }: { badgeData: PublicBadgeData }) => {
173
185
  <p>{developer_trust_explanation}</p>
174
186
  </div>
175
187
  <ProviderInsights
176
- platforms={connectedPlatforms || []}
188
+ platforms={connectedAccounts || []}
177
189
  insights={assessmentResult.provider_insights}
178
190
  />
179
191
  </div>
@@ -196,13 +208,15 @@ const SharedBadgeDisplay = ({ badgeData }: { badgeData: PublicBadgeData }) => {
196
208
  </div>
197
209
  ) : (
198
210
  <>
199
- <div className={'prose prose-sm max-w-none space-y-4 mb-6'} style={{ color: 'var(--text-secondary)' }}>
200
- <p>{riskScore?.description || ''}</p>
201
- </div>
211
+ {riskScore?.description && (
212
+ <div className={'prose prose-sm max-w-none space-y-4 mb-6'} style={{ color: 'var(--text-secondary)' }}>
213
+ <p>{riskScore.description}</p>
214
+ </div>
215
+ )}
202
216
  {(() => {
203
217
  const ss = assessmentResult?.screening_sources;
204
218
  const ofacMatches = ss?.ofac_screen?.matches && (ss.ofac_screen.matches.length > 0);
205
- const cslDetails = ss?.csl_details && (Array.isArray(ss.csl_details) ? ss.csl_details.length > 0 : true);
219
+ const cslDetails = ss?.csl_details && (Array.isArray(ss.csl_details) ? ss.csl_details.length > 0 : Object.keys(ss.csl_details).length > 0);
206
220
  const fbiMatches = ss?.fbi_matches && (ss.fbi_matches.length > 0);
207
221
  if (!(ofacMatches || cslDetails || fbiMatches)) return null;
208
222
  return (
@@ -4,19 +4,19 @@ import { FiInfo } from 'react-icons/fi';
4
4
  import { IpRiskAnalysis, IpRiskAnalysisFinding, IpRiskSections, IpRiskSection } from '../types';
5
5
 
6
6
  interface IpRiskAnalysisDisplayProps {
7
- ipRiskAnalysis?: IpRiskAnalysis;
7
+ ipRiskAnalysis?: IpRiskAnalysis;
8
8
  }
9
9
 
10
10
  const FindingRow = ({ finding }: { finding: IpRiskAnalysisFinding }) => (
11
- <div className="grid grid-cols-1 md:grid-cols-3 gap-x-8 gap-y-2 py-4 border-b" style={{ borderColor: 'var(--icon-button-secondary)' }}>
12
- <div className="md:col-span-1">
13
- <p className="font-semibold" style={{ color: 'var(--text-main)' }}>{finding.label}</p>
14
- </div>
15
- <div className="md:col-span-2">
16
- <p style={{ color: 'var(--text-secondary)' }}>{finding.details}</p>
17
- <p className="text-xs mt-1 italic" style={{ color: 'var(--text-secondary)' }}>{finding.implication}</p>
18
- </div>
11
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-x-8 gap-y-2 py-4 border-b" style={{ borderColor: 'var(--icon-button-secondary)' }}>
12
+ <div className="md:col-span-1">
13
+ <p className="font-semibold" style={{ color: 'var(--text-main)' }}>{finding.label}</p>
19
14
  </div>
15
+ <div className="md:col-span-2">
16
+ <p style={{ color: 'var(--text-secondary)' }}>{finding.details}</p>
17
+ <p className="text-xs mt-1 italic" style={{ color: 'var(--text-secondary)' }}>{finding.implication}</p>
18
+ </div>
19
+ </div>
20
20
  );
21
21
 
22
22
  const Section = ({ section }: { section: IpRiskSection }) => (
@@ -39,36 +39,36 @@ const Section = ({ section }: { section: IpRiskSection }) => (
39
39
  );
40
40
 
41
41
  const IpRiskAnalysisDisplay = ({ ipRiskAnalysis }: IpRiskAnalysisDisplayProps) => {
42
- if (!ipRiskAnalysis || !ipRiskAnalysis.checked || !ipRiskAnalysis.findings || ipRiskAnalysis.findings.length === 0) {
43
- return (
44
- <div className="mt-6 p-4 rounded-lg flex items-center" style={{ backgroundColor: 'var(--content-card-background)' }}>
45
- <FiInfo className="h-5 w-5 mr-3 flex-shrink-0" style={{ color: 'var(--text-secondary)' }} />
46
- <p className="text-sm" style={{ color: 'var(--text-secondary)' }}>IP risk analysis was not performed for this assessment.</p>
47
- </div>
48
- );
49
- }
50
-
51
- const { findings = [], sections } = ipRiskAnalysis as IpRiskAnalysis & { sections?: IpRiskSections };
42
+ if (!ipRiskAnalysis || !ipRiskAnalysis.checked || !ipRiskAnalysis.findings || ipRiskAnalysis.findings.length === 0) {
43
+ return (
44
+ <div className="mt-6 p-4 rounded-lg flex items-center" style={{ backgroundColor: 'var(--content-card-background)' }}>
45
+ <FiInfo className="h-5 w-5 mr-3 flex-shrink-0" style={{ color: 'var(--text-secondary)' }} />
46
+ <p className="text-sm" style={{ color: 'var(--text-secondary)' }}>IP risk analysis was not performed for this assessment.</p>
47
+ </div>
48
+ );
49
+ }
52
50
 
53
- if (sections) {
54
- return (
55
- <div className="mt-2 divide-y" style={{ borderColor: 'var(--icon-button-secondary)' }}>
56
- <Section section={sections.general} />
57
- <Section section={sections.location} />
58
- <Section section={sections.reputational} />
59
- </div>
60
- );
61
- }
51
+ const { findings = [], sections } = ipRiskAnalysis as IpRiskAnalysis & { sections?: IpRiskSections };
62
52
 
53
+ if (sections) {
63
54
  return (
64
- <div className="mt-6">
65
- <div className="divide-y" style={{ borderColor: 'var(--icon-button-secondary)' }}>
66
- {findings.map((finding, index) => (
67
- <FindingRow key={index} finding={finding} />
68
- ))}
69
- </div>
70
- </div>
55
+ <div className="mt-2 divide-y" style={{ borderColor: 'var(--icon-button-secondary)' }}>
56
+ <Section section={sections.general} />
57
+ <Section section={sections.location} />
58
+ <Section section={sections.reputational} />
59
+ </div>
71
60
  );
61
+ }
62
+
63
+ return (
64
+ <div className="mt-6">
65
+ <div className="divide-y" style={{ borderColor: 'var(--icon-button-secondary)' }}>
66
+ {findings.map((finding, index) => (
67
+ <FindingRow key={index} finding={finding} />
68
+ ))}
69
+ </div>
70
+ </div>
71
+ );
72
72
  };
73
73
 
74
74
  export default IpRiskAnalysisDisplay;
@@ -35,8 +35,9 @@ export default function ProviderInsights({ platforms, insights }: ProviderInsigh
35
35
  return (
36
36
  <div className="space-y-6">
37
37
  {platforms.map(platform => {
38
- const Icon = platform.name ? providerIcons[platform.name] : null;
39
- const providerInsight = insights[platform.name];
38
+ const normalizedName = platform.name || '';
39
+ const Icon = normalizedName ? (providerIcons[normalizedName] || null) : null;
40
+ const providerInsight = insights[normalizedName];
40
41
  const observedDate = platform.observedAt ? new Date(platform.observedAt).toLocaleDateString(undefined, {
41
42
  year: 'numeric',
42
43
  month: 'short',
@@ -84,7 +85,7 @@ export default function ProviderInsights({ platforms, insights }: ProviderInsigh
84
85
  </div>
85
86
 
86
87
  {/* Insights */}
87
- {providerInsight && (
88
+ {providerInsight && providerInsight.data_points && (
88
89
  <div className="p-4">
89
90
  {/* Summary */}
90
91
  <p className={'text-sm mb-4'} style={{ color: 'var(--text-secondary)' }}>
@@ -26,15 +26,15 @@ interface ReportHeaderProps {
26
26
  badgeId: string | undefined;
27
27
  developerName: string | undefined;
28
28
  updatedAt: string | undefined;
29
- score: number | undefined;
29
+ score?: number | undefined;
30
30
  isPublic: boolean;
31
31
  badgeImageUrl: string;
32
32
  }
33
33
 
34
34
  const ReportHeader = ({ badgeId, developerName, updatedAt, score = 0, isPublic, badgeImageUrl }: ReportHeaderProps) => {
35
35
  // Use the dynamic image if available, otherwise fall back to the score-based one.
36
- const finalBadgeImageUrl = badgeImageUrl || getBadgeImageUrl(score);
37
- const tint = hexToRgba(pickTint(score), 0.06);
36
+ const finalBadgeImageUrl = badgeImageUrl || getBadgeImageUrl(score || 0);
37
+ const tint = hexToRgba(pickTint(score || 0), 0.06);
38
38
 
39
39
  const formattedDate = updatedAt ? new Date(updatedAt).toLocaleString(undefined, {
40
40
  year: 'numeric',
package/src/types.ts CHANGED
@@ -26,7 +26,7 @@ export interface PublicBadgeData {
26
26
  assessmentResult: AssessmentResult;
27
27
  updatedAt: string;
28
28
  badgeImageUrl?: string;
29
- connectedPlatforms?: {
29
+ connectedAccounts?: {
30
30
  name: string;
31
31
  url?: string;
32
32
  handle?: string;
@@ -59,12 +59,12 @@ export type User = {
59
59
  latestBadgeImageUrl?: string;
60
60
  isPublic?: boolean;
61
61
  assessments: AssessmentSummary[];
62
- connectedPlatforms?: { name: string; url: string | null; handle: string | null; observedAt?: string }[];
62
+ connectedAccounts?: { name: string; url: string | null; handle: string | null; observedAt?: string }[];
63
63
  };
64
64
 
65
65
  export interface SummaryScore {
66
66
  score?: number;
67
- description: string;
67
+ description?: string;
68
68
  descriptor?: string;
69
69
  letter_grade?: string;
70
70
  }
@@ -80,6 +80,12 @@ export interface ProviderInsight {
80
80
  summary: string;
81
81
  }
82
82
 
83
+ export interface TopContributingRule {
84
+ provider: string;
85
+ rule_label: string;
86
+ why: string;
87
+ }
88
+
83
89
  export interface DomainCSVRow {
84
90
  Country?: string;
85
91
  'Entity Type'?: string;
@@ -128,25 +134,25 @@ export interface IpRiskAnalysis {
128
134
  }
129
135
 
130
136
  export interface AssessmentResult {
131
- report_summary: string;
137
+ report_summary?: string;
132
138
  recommendations: {
133
- summary: string;
134
- bullet_points: string[];
139
+ summary?: string;
140
+ bullet_points?: string[];
135
141
  };
136
- developer_trust_explanation: string;
137
- industry_considerations: string;
142
+ developer_trust_explanation?: string;
143
+ industry_considerations?: string;
138
144
  ai_usage_summary: {
139
- explanation: string;
140
- key_findings: string[];
141
- files_with_ai_findings: number;
142
- files_analyzed: number;
143
- };
144
- key_skills: string[];
145
- summary_scores: {
146
- developer_trust: SummaryScore;
147
- risk_score: SummaryScore;
148
- kyd_self_check: SummaryScore;
149
- ai_usage: SummaryScore;
145
+ explanation?: string;
146
+ key_findings?: string[];
147
+ files_with_ai_findings?: number;
148
+ files_analyzed?: number;
149
+ } | null;
150
+ key_skills?: string[];
151
+ summary_scores?: {
152
+ developer_trust?: SummaryScore;
153
+ risk_score?: SummaryScore;
154
+ kyd_self_check?: SummaryScore;
155
+ ai_usage?: SummaryScore;
150
156
  };
151
157
  provider_insights: {
152
158
  [provider: string]: ProviderInsight;
@@ -168,6 +174,9 @@ export interface AssessmentResult {
168
174
  csl_details?: any;
169
175
  fbi_matches?: any[];
170
176
  };
177
+ top_contributing_rules?: TopContributingRule[];
178
+ rules_graph_s3_key?: string;
179
+ rules_digest?: any;
171
180
  optOutScreening?: boolean;
172
181
  clientMetadata?: ClientMetadata;
173
182
  }