Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions apps/web/lib/blog-slugs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>): 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) {
Expand All @@ -37,8 +38,5 @@ export function closestSlug(slug: string, slugs: Iterable<string>): 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;
}
3 changes: 3 additions & 0 deletions test/blog-slugs.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Loading