export const guides={classic:'В обычном режиме Kernel создаётся заново на каждом запросе. Сравните bootId и handled: счётчик снова равен 1.',worker:'Один worker сохраняет Kernel между запросами. bootId остаётся прежним, handled растёт. Это жизненный цикл, а не обещание ускорения.',isolation:'Anna, Ivan и guest проходят через один Kernel. userBefore остаётся null, cacheBefore — 0 благодаря finally. Учебный query-параметр не является авторизацией.',json:'Успешный POST возвращает201, неверные данные422, сломанный JSON400. Ответ persisted:false честно сообщает, что заказ не сохранён.',errors:'Первый запрос намеренно вызывает исключение, второй успешно обрабатывается тем же worker. Клиент получает requestId, подробности остаются в журнале.',static:'asset.txt отдаёт Caddy. Несуществующий путь проходит через front controller и получает JSON404. Корень сайта отделён от исходников и конфигурации.',pdo:'Подключение PDO SQLite создано при загрузке Kernel. Проверенный числовой параметр передаётся в prepared statement. База демонстрационная, в памяти.',recycle:'После трёх запросов цикл worker завершается. FrankenPHP поднимает новый worker: меняется bootId, счётчик начинается заново. Это не автоматическая очистка ваших объектов.'};
export function verifyRecording(r){if(!r||r.schema!==1||typeof r.runtime?.version!=='string'||!Number.isFinite(Date.parse(r.generatedAt))||!r.sourceFiles||Object.values(r.sourceFiles).some(h=>typeof h!=='string'||!/^[a-f0-9]{64}$/.test(h))||!Array.isArray(r.runs)||r.runs.length!==8)throw Error('Неверный формат записи.');const seen=new Set();for(const run of r.runs){if(!Object.hasOwn(guides,run.id)||seen.has(run.id)||!Array.isArray(run.requests)||!run.requests.length||run.requests.length>30)throw Error('Неверный опыт в записи.');seen.add(run.id);for(const q of run.requests){if(typeof q.path!=='string'||q.path.length>300||typeof q.method!=='string'||!Number.isInteger(q.status)||q.status<100||q.status>599||!Number.isFinite(q.elapsedMs)||q.elapsedMs<0||q.body===undefined)throw Error('Неверный HTTP результат.');}}return r;}
export function config({mode='worker',target='local',domain='app.example.com',workers=2,maxRequests=1000}={}){if(!['worker','classic'].includes(mode)||!['local','domain'].includes(target))throw Error('Выберите режим и размещение.');if(mode==='worker'&&(!Number.isInteger(workers)||workers<1||workers>16||!Number.isInteger(maxRequests)||maxRequests<1||maxRequests>100000))throw Error('Workers: 1–16. Запросов до перезапуска: 1–100000.');if(target==='domain'&&!/^(?=.{4,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/.test(domain))throw Error('Введите домен без протокола, пути и порта.');const root=target==='local'?'site':'/srv/frankenphp/site';return`{\n    admin off\n${target==='local'?'    auto_https off\n    persist_config off\n':''}    frankenphp {\n        num_threads 2\n        php_ini display_errors 0\n        php_ini log_errors 1\n${mode==='worker'?`        worker {\n            file ${root}/index.php\n            num ${workers}\n            env FP_MODE worker\n            env MAX_REQUESTS ${maxRequests}\n        }\n`:''}    }\n}\n${target==='local'?'http://127.0.0.1:8420':domain} {\n${target==='local'?'    bind 127.0.0.1\n':''}    root * ${root}\n    encode zstd gzip\n    php_server\n}\n`;}
