import type { QueryCtx, MutationCtx } from './_generated/server.js'; export async function requireOwner(ctx: QueryCtx | MutationCtx) { const identity = await ctx.auth.getUserIdentity(); if (!identity) { throw new Error('Not authenticated'); } return identity.tokenIdentifier; } export function slugifyTag(value: string) { return value .trim() .toLowerCase() .replace(/['"]/g, '') .replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, '') .slice(0, 48); } export function cleanTagName(value: string) { return value.trim().replace(/\s+/g, ' ').slice(0, 40); } export function markdownToPlainText(markdown: string) { return markdown .replace(/```[\s\S]*?```/g, ' ') .replace(/`([^`]+)`/g, '$1') .replace(/!\[[^\]]*]\([^)]*\)/g, ' ') .replace(/\[([^\]]+)]\([^)]*\)/g, '$1') .replace(/[#>*_~`|[\](){}-]/g, ' ') .replace(/\s+/g, ' ') .trim(); } export function buildSearchText(args: { title: string; body: string; mood: string; tagNames: string[]; }) { return [args.title, markdownToPlainText(args.body), args.mood, ...args.tagNames] .filter(Boolean) .join(' '); } export function tagColor(slug: string) { const colors = ['amber', 'orange', 'rose', 'sky', 'teal', 'violet', 'lime', 'stone']; const total = [...slug].reduce((sum, char) => sum + char.charCodeAt(0), 0); return colors[total % colors.length]; }