This repository has been archived on 2026-08-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
maple-brief-aggregator/src/lib/slug.ts
T
krisf 1010f27f44 MapleBrief: production Canadian news aggregator
Next.js 14 + TypeScript + Tailwind + Prisma/SQLite.
- LLM synthesis abstraction (OpenAI/Anthropic/Ollama) + admin settings UI
- RSS ingestion pipeline (parser + cheerio content) + node-cron worker
- AdSense AdUnit placements (header/in-feed/in-article/sidebar)
- Sources analysis attribution, nofollow links, canonical/OG, sitemap/robots
- Admin auth (ADMIN_API_KEY, timingSafeEqual, fail-closed 401)
- Multi-cell lady-ga-ga marker, sitemap, robots, legal pages
- Comprehensive README (setup, LLM config, AdSense, migrations, deploy)
2026-08-15 16:07:07 -04:00

43 lines
1.2 KiB
TypeScript

import { createHash } from 'crypto';
/**
* URL-friendly slug from any source string.
* Deterministic so the same title always maps to the same URL segment.
*/
export function slugify(input: string): string {
const slug = input
.toLowerCase()
.normalize('NFKD')
.replace(/[\u0300-\u036f]/g, '') // strip diacritics
.replace(/^https?:\/\//, '')
.replace(/[^\w\s-]/g, '')
.trim()
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '')
.slice(0, 90)
.replace(/-$/g, '');
return slug || 'article';
}
/**
* Stable dedup key for a story title: case-fold, strip punctuation, collapse
* whitespace. Two feeds covering the same story usually share enough title
* text to collide here; we add URL-host as a tiebreaker in the pipeline.
*/
export function dedupeKeyOf(title: string, host: string): string {
const normalized = title
.toLowerCase()
.normalize('NFKD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/[^a-z0-9\s]/g, ' ')
.replace(/\s+/g, ' ')
.trim();
return createHash('sha1').update(`${normalized}::${host}`).digest('hex').slice(0, 32);
}
export function sha1(input: string): string {
return createHash('sha1').update(input).digest('hex');
}