From 452c0e8d364a36e716fc81cc7b2064ac55d7e9a6 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Sat, 17 Oct 2026 20:13:47 +0000 Subject: [PATCH] fix(blog): redirect old slugs to the same store's post, never another store's Co-Authored-By: Claude Sonnet 5.5 --- apps/web/lib/blog-slugs.ts | 14 ++++++-------- test/blog-slugs.test.mjs | 3 +++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apps/web/lib/blog-slugs.ts b/apps/web/lib/blog-slugs.ts index 1ee76b0..39ae846 100644 --- a/apps/web/lib/blog-slugs.ts +++ b/apps/web/lib/blog-slugs.ts @@ -20,14 +20,15 @@ const lead = (s: string) => s.split('-')[0]; /** * The published slug an old or mistyped one meant: the closest by edit distance - * within a third of its length; failing that, the only slug sharing the leading - * store name (doordash-..., shutterfly-..., walgreens-...), else the shortest - * edit distance among those sharing it. Null when nothing plausible exists. + * after the post for the same store (shared leading name: walgreens-...), then + * a near-identical typo within a fifth of its length. Null when nothing plausible exists. */ export function closestSlug(slug: string, slugs: Iterable): string | null { const all = [...slugs]; if (all.includes(slug)) return slug; - const max = Math.max(3, Math.floor(slug.length / 3)); + const sameStore = all.filter((s) => lead(s) === lead(slug) && lead(slug).length >= 4); + if (sameStore.length) return sameStore.sort((a, b) => editDistance(slug, a) - editDistance(slug, b))[0]; + const max = Math.max(3, Math.floor(slug.length / 5)); let best: string | null = null; let bestD = max + 1; for (const s of all) { @@ -37,8 +38,5 @@ export function closestSlug(slug: string, slugs: Iterable): string | nul bestD = d; } } - if (best) return best; - const sameStore = all.filter((s) => lead(s) === lead(slug) && lead(slug).length >= 4); - if (!sameStore.length) return null; - return sameStore.sort((a, b) => editDistance(slug, a) - editDistance(slug, b))[0]; + return best; } diff --git a/test/blog-slugs.test.mjs b/test/blog-slugs.test.mjs index be478bb..1008746 100644 --- a/test/blog-slugs.test.mjs +++ b/test/blog-slugs.test.mjs @@ -12,6 +12,9 @@ describe('closestSlug', () => { it('maps an old slug to the post of the same store', () => { expect(closestSlug('walgreens-coupon-code-workflow', SLUGS)).toBe('walgreens-photo-coupon-code-workflow-2026'); }); + it('never crosses stores', () => { + expect(closestSlug('shutterfly-promo-codes-2026-checkout-workflow', SLUGS)).toBeNull(); + }); it('keeps an existing slug and returns null when no post is close', () => { expect(closestSlug('cvs-photo-promo-code-workflow', SLUGS)).toBe('cvs-photo-promo-code-workflow'); expect(closestSlug('doordash-promo-codes-for-existing-users-2026-workflow', SLUGS)).toBeNull();