All files / server/utils llms-document.ts

100% Statements 75/75
93.33% Branches 28/30
100% Functions 4/4
100% Lines 75/75

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101                    1x 76x 76x 76x 76x   1x 25x   1x           5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x   5x 4x 4x 4x   5x 4x 4x 4x 4x 4x 4x 4x 4x 2x 2x 4x 4x   5x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x   5x 1x 1x   5x 5x   1x 1x 1x 1x 1x     1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x  
import type {Category, Product, Universe} from '~/types/shop'
 
type LlmSiteContent = {
    seoSiteName?: string
    seoDescription?: string
    seoOrganizationName?: string
    seoOrganizationEmail?: string
    seoLanguage?: string
}
 
const clean = (value: unknown) => String(value ?? '')
    .replace(/[\r\n\t]+/g, ' ')
    .replace(/\s{2,}/g, ' ')
    .replace(/[\[\]]/g, '')
    .trim()
 
const link = (label: unknown, url: string, description?: unknown) =>
    `- [${clean(label)}](${url})${clean(description) ? `: ${clean(description)}` : ''}`
 
export function formatLlmsDocument(input: {
    origin: string
    content: LlmSiteContent
    products: Product[]
    categories: Category[]
    universes: Universe[]
}) {
    const {origin, content, products, categories, universes} = input
    const siteName = clean(content.seoSiteName || content.seoOrganizationName || 'Boutique en ligne')
    const lines = [
        `# ${siteName}`,
        '',
        `> ${clean(content.seoDescription || 'Boutique en ligne de produits physiques.')}`,
        '',
        `Langue principale : ${clean(content.seoLanguage || 'fr')}.`,
        'Les produits présentés sont des articles physiques vendus en ligne.',
        '',
        '## Pages principales',
        '',
        link('Accueil', `${origin}/`, content.seoDescription),
        link('Plan du site XML', `${origin}/sitemap.xml`)
    ]
 
    if (categories.length) {
        lines.push('', '## Catégories', '')
        for (const category of categories) lines.push(link(category.label, `${origin}/categories/${encodeURIComponent(category.slug)}`))
    }
 
    if (universes.length) {
        lines.push('', '## Univers', '')
        for (const universe of universes) {
            const universeKey = encodeURIComponent(universe.slug || String(universe.id))
            lines.push(link(universe.title, `${origin}/univers/${universeKey}`))
            const categoryIds = new Set(products
                .filter(product => product.universeIds.includes(universe.id))
                .flatMap(product => product.categoryIds))
            for (const category of categories.filter(item => categoryIds.has(item.id))) {
                lines.push(`  ${link(`${universe.title} — ${category.label}`, `${origin}/univers/${universeKey}/${encodeURIComponent(category.slug)}`)}`)
            }
        }
    }
 
    if (products.length) {
        lines.push('', '## Produits', '')
        for (const product of products) {
            const price = `${(product.priceCents / 100).toFixed(2).replace('.', ',')} €`
            const context = [product.description, `Prix : ${price}`,
                product.categories.length ? `Catégories : ${product.categories.map(item => item.label).join(', ')}` : '',
                product.universes.length ? `Univers : ${product.universes.map(item => item.title).join(', ')}` : '']
                .filter(Boolean).join(' — ')
            lines.push(link(product.name, `${origin}/produits/${encodeURIComponent(product.slug)}`, context))
        }
    }
 
    if (clean(content.seoOrganizationEmail)) {
        lines.push('', '## Contact', '', `- Email : ${clean(content.seoOrganizationEmail)}`)
    }
 
    return `${lines.join('\n')}\n`
}
 
export async function buildLlmsDocument(event: any) {
    const db = database(event)
    await ready(db)
    const {results: contentRows} = await db.prepare("SELECT `key`,value FROM site_content WHERE `key` IN ('seoSiteName','seoDescription','seoOrganizationName','seoOrganizationEmail','seoLanguage','seoCanonicalUrl')").all<any>()
    const content = Object.fromEntries(contentRows.map(row => [row.key, row.value])) as LlmSiteContent & {
        seoCanonicalUrl?: string
    }
    const origin = String(content.seoCanonicalUrl || useRuntimeConfig(event).public.siteUrl || getRequestURL(event).origin).replace(/\/$/, '')
    const {results: productRows} = await db.prepare(`${productSelect} WHERE p.active=1 ORDER BY p.featured DESC,p.featured_position,p.id`).all<any>()
    const products = await mapProductsWithRelations(db, productRows)
    const {results: categoryRows} = await db.prepare('SELECT * FROM categories WHERE active=1 ORDER BY position,id').all<any>()
    const {results: universeRows} = await db.prepare(`${universeSelect} WHERE u.active=1 ORDER BY u.position,u.id`).all<any>()
    return formatLlmsDocument({
        origin,
        content,
        products,
        categories: categoryRows.map(mapCategory),
        universes: universeRows.map(mapUniverse)
    })
}