From 577c10e6eb9e62543807230493d97b39b20aa0d2 Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Tue, 3 Mar 2026 15:47:12 +0000 Subject: [PATCH 1/8] chore: add a little extra validation (#1894) --- .../api/registry/badge/[type]/[...pkg].get.ts | 28 +++++++++++++------ shared/schemas/social.ts | 14 +++++++++- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/server/api/registry/badge/[type]/[...pkg].get.ts b/server/api/registry/badge/[type]/[...pkg].get.ts index fdc994233c..cbab8ac374 100644 --- a/server/api/registry/badge/[type]/[...pkg].get.ts +++ b/server/api/registry/badge/[type]/[...pkg].get.ts @@ -87,6 +87,14 @@ function measureDefaultTextWidth(text: string): number { return Math.max(MIN_BADGE_TEXT_WIDTH, Math.round(text.length * CHAR_WIDTH) + BADGE_PADDING_X * 2) } +function escapeXML(str: string): string { + return str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') +} + function measureShieldsTextLength(text: string): number { const measuredWidth = measureTextWidth(text, SHIELDS_FONT_SHORTHAND) @@ -108,9 +116,11 @@ function renderDefaultBadgeSvg(params: { const rightWidth = measureDefaultTextWidth(finalValue) const totalWidth = leftWidth + rightWidth const height = 20 + const escapedLabel = escapeXML(finalLabel) + const escapedValue = escapeXML(finalValue) return ` - + @@ -119,8 +129,8 @@ function renderDefaultBadgeSvg(params: { - ${finalLabel} - ${finalValue} + ${escapedLabel} + ${escapedValue} `.trim() @@ -141,7 +151,9 @@ function renderShieldsBadgeSvg(params: { const rightWidth = rightTextLength + SHIELDS_LABEL_PADDING_X * 2 const totalWidth = leftWidth + rightWidth const height = 20 - const title = `${finalLabel}: ${finalValue}` + const escapedLabel = escapeXML(finalLabel) + const escapedValue = escapeXML(finalValue) + const title = `${escapedLabel}: ${escapedValue}` const leftCenter = Math.round((leftWidth / 2) * 10) const rightCenter = Math.round((leftWidth + rightWidth / 2) * 10) @@ -163,10 +175,10 @@ function renderShieldsBadgeSvg(params: { - - ${finalLabel} - - ${finalValue} + + ${escapedLabel} + + ${escapedValue} `.trim() diff --git a/shared/schemas/social.ts b/shared/schemas/social.ts index f0c018e11d..1e3305941b 100644 --- a/shared/schemas/social.ts +++ b/shared/schemas/social.ts @@ -13,7 +13,19 @@ export type PackageLikeBody = v.InferOutput // TODO: add 'avatar' export const ProfileEditBodySchema = v.object({ displayName: v.pipe(v.string(), v.maxLength(640)), - website: v.optional(v.union([v.literal(''), v.pipe(v.string(), v.url())])), + website: v.optional( + v.union([ + v.literal(''), + v.pipe( + v.string(), + v.url(), + v.check( + url => url.startsWith('https://') || url.startsWith('http://'), + 'Website must use http or https', + ), + ), + ]), + ), description: v.optional(v.pipe(v.string(), v.maxLength(2560))), }) From 1d5feeeb680d677c3049ac3148e4dd43e06e27a3 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 3 Mar 2026 15:57:31 +0000 Subject: [PATCH 2/8] fix: handle custom canary environment --- config/env.ts | 8 +++++--- test/unit/config/env.spec.ts | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/config/env.ts b/config/env.ts index cbd852d406..ec7cd7ed16 100644 --- a/config/env.ts +++ b/config/env.ts @@ -44,13 +44,15 @@ export const gitBranch = process.env.BRANCH || process.env.VERCEL_GIT_COMMIT_REF * Whether this is the canary environment (main.npmx.dev). * * Detected as any non-PR Vercel deploy from the `main` branch - * (which may receive `VERCEL_ENV === 'production'` or `'preview'` - * depending on the project's production branch configuration). + * (which may receive `VERCEL_ENV === 'production'`, `'preview'`, or a + * custom `'canary'` environment depending on the project configuration). * * @see {@link https://vercel.com/docs/environment-variables/system-environment-variables#VERCEL_ENV} */ export const isCanary = - (process.env.VERCEL_ENV === 'production' || process.env.VERCEL_ENV === 'preview') && + (process.env.VERCEL_ENV === 'production' || + process.env.VERCEL_ENV === 'preview' || + process.env.VERCEL_ENV === 'canary') && gitBranch === 'main' && !isPR diff --git a/test/unit/config/env.spec.ts b/test/unit/config/env.spec.ts index 48be8159b8..3d3fb0b4ec 100644 --- a/test/unit/config/env.spec.ts +++ b/test/unit/config/env.spec.ts @@ -43,6 +43,14 @@ describe('isCanary', () => { expect(isCanary).toBe(true) }) + it('returns true when VERCEL_ENV is custom "canary" and branch is "main"', async () => { + vi.stubEnv('VERCEL_ENV', 'canary') + vi.stubEnv('VERCEL_GIT_COMMIT_REF', 'main') + const { isCanary } = await import('../../../config/env') + + expect(isCanary).toBe(true) + }) + it('returns false when VERCEL_ENV is "preview", branch is "main", but is a PR', async () => { vi.stubEnv('VERCEL_ENV', 'preview') vi.stubEnv('VERCEL_GIT_COMMIT_REF', 'main') @@ -97,6 +105,15 @@ describe('getEnv', () => { expect(result.env).toBe('canary') }) + it('returns "canary" for custom Vercel "canary" environment on main branch', async () => { + vi.stubEnv('VERCEL_ENV', 'canary') + vi.stubEnv('VERCEL_GIT_COMMIT_REF', 'main') + const { getEnv } = await import('../../../config/env') + const result = await getEnv(false) + + expect(result.env).toBe('canary') + }) + it('returns "preview" for Vercel preview PR deploys', async () => { vi.stubEnv('VERCEL_ENV', 'preview') vi.stubEnv('VERCEL_GIT_PULL_REQUEST_ID', '123') From 002099971923e8ba88ecd402a06e3d681a530c9c Mon Sep 17 00:00:00 2001 From: Florian <45694132+flo-bit@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:08:20 +0100 Subject: [PATCH 3/8] chore: add blento to OSS Partners (#1897) --- app/assets/logos/oss-partners/blento.svg | 6 ++++++ app/assets/logos/oss-partners/index.ts | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 app/assets/logos/oss-partners/blento.svg diff --git a/app/assets/logos/oss-partners/blento.svg b/app/assets/logos/oss-partners/blento.svg new file mode 100644 index 0000000000..38b4792e7f --- /dev/null +++ b/app/assets/logos/oss-partners/blento.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/assets/logos/oss-partners/index.ts b/app/assets/logos/oss-partners/index.ts index 4f14698d4d..e27f78f6b7 100644 --- a/app/assets/logos/oss-partners/index.ts +++ b/app/assets/logos/oss-partners/index.ts @@ -30,6 +30,7 @@ import LogoLunaria from './lunaria.svg' import LogoJsr from './jsr.svg' import LogoIconify from './iconify.svg' import LogoFloatingUi from './floating-ui-vue.svg' +import LogoBlento from './blento.svg' // The list is used on the about page. To add, simply upload the logos nearby and add an entry here. Prefer SVGs. // For logo src, specify a string or object with the light and dark theme variants. @@ -195,4 +196,9 @@ export const OSS_PARTNERS = [ logo: LogoFloatingUi, url: 'https://floating-ui.com/', }, + { + name: 'blento', + logo: LogoBlento, + url: 'https://blento.app/npmx.dev', + }, ] From 75255f17103e249654cfa718e5b502dd4be70001 Mon Sep 17 00:00:00 2001 From: Bailey Townsend Date: Tue, 3 Mar 2026 11:20:14 -0600 Subject: [PATCH 4/8] fix: standard.site fixes (#1901) --- app/components/global/BlogPostWrapper.vue | 10 ++++ modules/standard-site-sync.ts | 46 ++++++------------- nuxt.config.ts | 1 + package.json | 2 +- pnpm-lock.yaml | 35 ++++++++++++-- .../site.standard.publication.get.ts | 6 +++ server/utils/atproto/utils/likes.ts | 6 +-- shared/utils/atproto.ts | 31 +++++++++++++ shared/utils/constants.ts | 2 + .../unit/server/utils/likes-evolution.spec.ts | 4 +- 10 files changed, 102 insertions(+), 41 deletions(-) create mode 100644 server/routes/.well-known/site.standard.publication.get.ts create mode 100644 shared/utils/atproto.ts diff --git a/app/components/global/BlogPostWrapper.vue b/app/components/global/BlogPostWrapper.vue index b2651f0845..d50ad9b8dc 100644 --- a/app/components/global/BlogPostWrapper.vue +++ b/app/components/global/BlogPostWrapper.vue @@ -1,5 +1,6 @@