'use strict'; const MAX_INPUT = 8192; function webURL(value) { if (typeof value !== 'string' || value.length > MAX_INPUT) throw new Error('Invalid URL'); const url = new URL(value); if (!['https:', 'http:'].includes(url.protocol) || !url.hostname || url.username || url.password) throw new Error('Only HTTP and HTTPS addresses without embedded credentials are supported'); return url.href; } function navigation(input) { if (typeof input !== 'string' || input.length > MAX_INPUT) throw new Error('Address is too long'); const value = input.trim(); if (!value || value === 'infi://newtab') return 'infi://newtab'; if (/^https?:\/\//i.test(value)) return webURL(value); if (/^(localhost|\[[a-f\d:]+\]|[\w.-]+\.[a-z\d-]{2,})(:\d+)?([/?#].*)?$/i.test(value) && !/\s/.test(value)) return webURL(`https://${value}`); if (/^[a-z][a-z\d+.-]*:/i.test(value)) throw new Error('This address type is not supported'); return `https://duckduckgo.com/?q=${encodeURIComponent(value)}`; } function validatePlugin(raw) { if (!raw || Array.isArray(raw) || typeof raw !== 'object') throw new Error('Manifest must be an object'); if (JSON.stringify(raw).length > 32768) throw new Error('Manifest exceeds 32 KB'); const allowed = ['manifest_version','id','name','version','description','permissions','commands']; if (Object.keys(raw).some(k => !allowed.includes(k))) throw new Error('Unknown manifest field'); if (raw.manifest_version !== 1 || !/^[a-z][a-z0-9.-]{2,79}$/.test(raw.id ?? '')) throw new Error('Invalid manifest version or plugin ID'); if (typeof raw.name !== 'string' || !raw.name.trim() || raw.name.length > 80) throw new Error('Name is required (80 characters maximum)'); if (typeof raw.version !== 'string' || !/^\d+\.\d+\.\d+$/.test(raw.version)) throw new Error('Version must use major.minor.patch'); if (typeof raw.description !== 'string' || raw.description.length > 300) throw new Error('Description is required (300 characters maximum)'); if (!Array.isArray(raw.permissions) || !raw.permissions.includes('tabs.create') || raw.permissions.some(p => !['tabs.create','activeTab'].includes(p)) || new Set(raw.permissions).size !== raw.permissions.length) throw new Error('Unknown or missing permission'); if (!Array.isArray(raw.commands) || !raw.commands.length || raw.commands.length > 12) throw new Error('Provide 1–12 commands'); const ids = new Set(); const commands = raw.commands.map(c => { if (!c || Object.keys(c).some(k => !['id','title','url'].includes(k))) throw new Error('Unknown command field'); if (typeof c.id !== 'string' || !/^[a-z][a-z0-9-]{0,39}$/.test(c.id) || ids.has(c.id)) throw new Error('Command IDs must be unique'); ids.add(c.id); if (typeof c.title !== 'string' || !c.title.trim() || c.title.length > 80 || typeof c.url !== 'string') throw new Error('Invalid command title or URL'); if (/\{(?!query\}|url\}|title\})/.test(c.url) || c.url.replace(/\{(query|url|title)\}/g,'').includes('}')) throw new Error('Unknown template placeholder'); if (/\{(url|title)\}/.test(c.url) && !raw.permissions.includes('activeTab')) throw new Error('Page context requires activeTab permission'); const u = new URL(webURL(c.url.replace(/\{(query|url|title)\}/g,'example'))); if (u.protocol !== 'https:' || /\{/.test(c.url.split('/').slice(0,3).join('/'))) throw new Error('Commands require a fixed HTTPS origin'); return {id:c.id,title:c.title,url:c.url}; }); return {manifest_version:1,id:raw.id,name:raw.name,version:raw.version,description:raw.description,permissions:[...raw.permissions],commands}; } function commandURL(manifest, commandId, context = {}) { const p = validatePlugin(manifest); const c = p.commands.find(c => c.id === commandId); if (!c) throw new Error('Unknown command'); if (context.private && /\{(url|title)\}/.test(c.url)) throw new Error('Page-sharing commands are disabled in private tabs'); const data = {query:String(context.query ?? '').slice(0,2048),url:'',title:''}; if (p.permissions.includes('activeTab')) { data.url = context.url && context.url !== 'infi://newtab' ? webURL(context.url) : ''; data.title = String(context.title ?? '').slice(0,512); } return webURL(c.url.replace(/\{(query|url|title)\}/g, (_, k) => encodeURIComponent(data[k]))); } module.exports = {webURL,navigation,validatePlugin,commandURL};