'use strict'; const crypto = require('node:crypto'); const SCHEME = 'mangacircus-work-v1'; const KINDS = new Set(['anilist', 'mangadex', 'weebcentral', 'hoster', 'origin']); function normalizeWorkIdentity(value) { if (!value || typeof value !== 'object' || value.scheme !== SCHEME || !KINDS.has(value.kind) || typeof value.value !== 'string' || !value.value.trim() || value.value.length > 300) return null; return { scheme: SCHEME, kind: value.kind, value: value.value.trim() }; } function resolveWorkIdentity(meta = {}, mangaId, serverId) { const carried = normalizeWorkIdentity(meta['x-work-identity']); if (carried) return carried; const anilist = meta['x-anilist']?.id ?? meta['x-scraper']?.ids?.anilist; if (anilist != null && String(anilist).trim()) return { scheme: SCHEME, kind: 'anilist', value: String(anilist).trim() }; const ids = meta['x-scraper']?.ids || {}; for (const source of ['mangadex', 'weebcentral']) { if (ids[source] != null && String(ids[source]).trim()) { return { scheme: SCHEME, kind: source, value: String(ids[source]).trim() }; } } for (const source of Object.keys(ids).sort()) { if (!['anilist', 'mangadex', 'weebcentral'].includes(source) && !source.startsWith('peer-') && ids[source] != null && String(ids[source]).trim()) { return { scheme: SCHEME, kind: 'hoster', value: `${source}:${String(ids[source]).trim()}` }; } } // A private, stable lineage ID lets a title safely make a round trip even if // neither server has external metadata. It reveals neither the folder nor title. if (typeof mangaId === 'string' && mangaId && typeof serverId === 'string' && serverId) { const value = crypto.createHash('sha256').update(`${serverId}\0${mangaId}`).digest('hex'); return { scheme: SCHEME, kind: 'origin', value }; } return null; } function sameWorkIdentity(left, right) { const a = normalizeWorkIdentity(left), b = normalizeWorkIdentity(right); return !!a && !!b && a.scheme === b.scheme && a.kind === b.kind && a.value === b.value; } module.exports = { SCHEME, normalizeWorkIdentity, resolveWorkIdentity, sameWorkIdentity };