Skip to content

Commit 78abae3

Browse files
idoshamunclaude
andauthored
feat: add social platform detection and link limit (#3431)
Co-authored-by: Claude <noreply@anthropic.com>
1 parent a219186 commit 78abae3

2 files changed

Lines changed: 74 additions & 1 deletion

File tree

__tests__/common/socials.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { detectPlatformFromUrl } from '../../src/common/schema/socials';
1+
import {
2+
detectPlatformFromUrl,
3+
socialLinksInputSchema,
4+
MAX_SOCIAL_LINKS,
5+
} from '../../src/common/schema/socials';
26

37
describe('detectPlatformFromUrl', () => {
48
it('should detect twitter.com', () => {
@@ -90,3 +94,37 @@ describe('detectPlatformFromUrl', () => {
9094
expect(detectPlatformFromUrl('')).toBeNull();
9195
});
9296
});
97+
98+
describe('socialLinksInputSchema', () => {
99+
it('should accept up to MAX_SOCIAL_LINKS links', () => {
100+
const links = Array.from({ length: MAX_SOCIAL_LINKS }, (_, i) => ({
101+
url: `https://github.com/user${i}`,
102+
}));
103+
const result = socialLinksInputSchema.safeParse(links);
104+
expect(result.success).toBe(true);
105+
});
106+
107+
it('should reject more than MAX_SOCIAL_LINKS links', () => {
108+
const links = Array.from({ length: MAX_SOCIAL_LINKS + 1 }, (_, i) => ({
109+
url: `https://github.com/user${i}`,
110+
}));
111+
const result = socialLinksInputSchema.safeParse(links);
112+
expect(result.success).toBe(false);
113+
});
114+
115+
it('should auto-detect platforms for new platforms', () => {
116+
const links = [
117+
{ url: 'https://codeberg.org/testuser' },
118+
{ url: 'https://gitlab.com/testuser' },
119+
{ url: 'https://bitbucket.org/testuser' },
120+
{ url: 'https://kaggle.com/testuser' },
121+
];
122+
const result = socialLinksInputSchema.parse(links);
123+
expect(result).toEqual([
124+
{ platform: 'codeberg', url: 'https://codeberg.org/testuser' },
125+
{ platform: 'gitlab', url: 'https://gitlab.com/testuser' },
126+
{ platform: 'bitbucket', url: 'https://bitbucket.org/testuser' },
127+
{ platform: 'kaggle', url: 'https://kaggle.com/testuser' },
128+
]);
129+
});
130+
});

src/common/schema/socials.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,22 @@ export const blueskySchema = socialUrlSchema(
6565
/^(?:(?:https:\/\/)?(?:www\.)?bsky\.app\/profile\/)?(?<value>[\w.-]+)(?:\/.*)?$/,
6666
);
6767

68+
export const codebergSchema = socialUrlSchema(
69+
/^(?:(?:https:\/\/)?(?:www\.)?codeberg\.org\/)?(?<value>[\w-]{2,})\/?$/,
70+
);
71+
72+
export const gitlabSchema = socialUrlSchema(
73+
/^(?:(?:https:\/\/)?(?:www\.)?gitlab\.com\/)?(?<value>[\w-]{2,})\/?$/,
74+
);
75+
76+
export const bitbucketSchema = socialUrlSchema(
77+
/^(?:(?:https:\/\/)?(?:www\.)?bitbucket\.org\/)?(?<value>[\w-]{2,})\/?$/,
78+
);
79+
80+
export const kaggleSchema = socialUrlSchema(
81+
/^(?:(?:https:\/\/)?(?:www\.)?kaggle\.com\/)?(?<value>[\w-]{2,})\/?$/,
82+
);
83+
6884
export const socialFieldsSchema = z.object({
6985
github: githubSchema,
7086
twitter: twitterSchema,
@@ -101,6 +117,10 @@ const PLATFORM_DOMAINS: Record<string, string> = {
101117
'youtu.be': 'youtube',
102118
'hashnode.com': 'hashnode',
103119
'hashnode.dev': 'hashnode',
120+
'codeberg.org': 'codeberg',
121+
'gitlab.com': 'gitlab',
122+
'bitbucket.org': 'bitbucket',
123+
'kaggle.com': 'kaggle',
104124
};
105125

106126
/**
@@ -143,11 +163,14 @@ export const socialLinkInputSchema = z.object({
143163
platform: z.string().optional(),
144164
});
145165

166+
export const MAX_SOCIAL_LINKS = 20;
167+
146168
/**
147169
* Schema for socialLinks array input with auto-detection and transformation
148170
*/
149171
export const socialLinksInputSchema = z
150172
.array(socialLinkInputSchema)
173+
.max(MAX_SOCIAL_LINKS)
151174
.transform((links) =>
152175
links.map(({ url, platform }) => ({
153176
platform: platform || detectPlatformFromUrl(url) || 'other',
@@ -212,6 +235,18 @@ export function extractHandleFromUrl(
212235
case 'portfolio':
213236
// Full URL is stored for portfolio
214237
return url;
238+
case 'codeberg':
239+
// https://codeberg.org/username
240+
return pathname.replace(/^\//, '') || null;
241+
case 'gitlab':
242+
// https://gitlab.com/username
243+
return pathname.replace(/^\//, '') || null;
244+
case 'bitbucket':
245+
// https://bitbucket.org/username
246+
return pathname.replace(/^\//, '') || null;
247+
case 'kaggle':
248+
// https://kaggle.com/username
249+
return pathname.replace(/^\//, '') || null;
215250
default:
216251
return null;
217252
}

0 commit comments

Comments
 (0)