import fs from 'node:fs'; import path from 'node:path'; const root=process.cwd(); const pub=path.join(root,'public'); const readPub=p=>fs.readFileSync(path.join(pub,p),'utf8'); const readRoot=p=>fs.readFileSync(path.join(root,p),'utf8'); const fail=m=>{console.error('FAIL:',m);process.exitCode=1}; const ok=m=>console.log('OK:',m); const html=readPub('index.html'),legacy=readPub('legacy/bootstrap.js'),runtime=readPub('app-runtime.js'),safe=readPub('core/sun-safe.js'),sw=readPub('service-worker.js'),performance=readPub('core/performance.js'),dataLayer=readPub('core/data-layer-v1773.js'),serverAutomation=readPub('core/server-automation-v1770.js'),ops=readPub('core/ops-ux-v1762.js'),hotfix=readPub('core/hotfix-v1763.js'),ux=readPub('core/ux-fixes-v1764.js'),classic=readPub('core/classic-offer-pdf-v1767.js'),developerUX=readPub('core/developer-console-v1768.js'),offerWorkspace=readPub('core/offer-workspace-v1769.js'); if(!html.includes('core/sun-safe.js'))fail('SunSafe must load before legacy modules');else ok('shared SunSafe loaded'); if(html.includes('offer-gallery-data.js')||fs.existsSync(path.join(pub,'offer-gallery-data.js')))fail('blocking offer-gallery-data.js still present');else ok('base64 gallery removed'); for(const raw of ['${b.name}','${o.event}','${o.address||','value="${x[0]}"','value="${x[2]}"']) if(legacy.includes(raw)) fail(`legacy bootstrap contains raw HTML interpolation ${raw}`); if(!legacy.includes('sunEsc(')||!legacy.includes('sunAttr('))fail('legacy bootstrap is not using shared escaping');else ok('legacy bootstrap escapes text and attributes'); if(!legacy.includes("persistOrders('order.save')")||!legacy.includes("persistOrders('order.status')")||!legacy.includes("persistOrders('order.delete')")||!legacy.includes("persistCatalog('catalog.save')")||!legacy.includes("persistCatalog('catalog.delete')"))fail('legacy CRUD is not routed through data layer');else ok('legacy CRUD routes through data layer'); const duplicateEsc=[html,runtime].join('\n').split('\n').filter(line=>/\b(?:const|let)\s+esc\b/.test(line)&&line.includes('.replace(')); if(duplicateEsc.length)fail(`duplicate esc implementations remain: ${duplicateEsc.length}`);else ok('local esc aliases delegate to SunSafe'); if(runtime.includes('/Type /Catalog'))fail('manual PDF writer remains in app-runtime.js');else ok('PDF packing removed from runtime'); if(!readPub('core/pdf-engine.js').includes('/Type /Catalog'))fail('shared PDF engine missing');else ok('single shared PDF engine present'); const employeeEdge=path.join(root,'supabase/functions/caterium-create-employee/index.ts'); if(!fs.existsSync(employeeEdge))fail('production employee Edge Function source missing');else ok('production employee Edge Function versioned'); const edge=readRoot('supabase/functions/caterium-create-employee/index.ts'); if(/service_role\s*[:=]\s*['"][A-Za-z0-9._-]{20,}/i.test(edge)||/eyJ[a-zA-Z0-9_-]{30,}/.test(edge))fail('possible hard-coded secret in Edge Function');else ok('no hard-coded service key detected'); if(!safe.includes('reference.parentNode===parent'))fail('safe insertBefore guard missing');else ok('safe DOM insertion guard present'); const files=fs.readdirSync(path.join(pub,'catalog')); const current=files.filter(x=>/^current-\d{3}\.jpg$/i.test(x)).length; const legacyCount=files.filter(x=>/^\d{3}\.jpg$/i.test(x)).length; if(current!==113)fail(`current catalog photo count ${current}, expected 113`);else ok('113 current catalog photos'); if(legacyCount!==60)fail(`legacy catalog photo count ${legacyCount}, expected 60`);else ok('60 legacy catalog photos'); const gallery=fs.readdirSync(path.join(pub,'offer-gallery')).filter(x=>/\.jpg$/i.test(x)); if(gallery.length!==2)fail(`offer gallery contains ${gallery.length} jpg files, expected 2`);else ok('offer gallery trimmed'); if(!sw.includes('v17-7-3-clients-server-read')||!sw.includes('data-layer-v1773.js')||!sw.includes('server-automation-v1770.js')||!sw.includes('offer-workspace-v1769.js')||sw.includes('offer-gallery-data.js'))fail('service worker cache is stale');else ok('PWA cache updated to v17.7.0'); if(html.includes('20260907-v17-6-0-stability-security')||!html.includes('20260909-v17-7-3-clients-server-read')||!html.includes('classic-offer-pdf-v1767.js'))fail('index still serves stale core asset version');else ok('index cache-busting is current'); if(!performance.includes('SunAttachmentGuard')||!performance.includes('MAX_SIDE=2048'))fail('chat photo compression guard missing');else ok('chat photo compression guard present'); if(!performance.includes("rpc('sun_dev_dashboard')")||!performance.includes('storage_size')||!performance.includes('server_size'))fail('Developer Console memory counters missing');else ok('Developer Console memory counters present'); if(performance.includes('records.forEach(r=>r.addedNodes.forEach(n=>{if(n.nodeType===1)scan(n)}));enhanceDeveloperMemory()'))fail('Developer Console memory refresh is still coupled to MutationObserver');else ok('Developer Console memory refresh loop removed'); if(!performance.includes('MEMORY_REFRESH_MS=30000')||!performance.includes('MEMORY_TIMEOUT_MS=8000')||!performance.includes('memoryPromise'))fail('Developer Console bounded refresh controls missing');else ok('Developer Console bounded refresh controls present'); if(!performance.includes('pendingImageRoots')||!performance.includes('queueImageScan'))fail('batched image mutation scanning missing');else ok('image mutation scanning is batched'); if(!performance.includes('loadDataLayer')||!performance.includes('data-layer-v1773.js')||!performance.includes('loadServerAutomation'))fail('v17.7.1 data layer loaders missing');else ok('v17.7.1 data layer loaders present'); if(!dataLayer.includes("const VERSION='17.7.3'")||!dataLayer.includes('ordersRepo')||!dataLayer.includes('catalogRepo')||!dataLayer.includes('clientsRepo')||!dataLayer.includes('sun_v17_save_client_v1772')||!dataLayer.includes('sun_v17_clients_snapshot_v1773')||!dataLayer.includes('refreshServerClients')||!dataLayer.includes('CateriumDataV1772=api'))fail('v17.7.3 data layer missing');else ok('v17.7.3 data layer present'); if(!serverAutomation.includes("const VERSION='17.7.0'")||!serverAutomation.includes("rpc('sun_run_order_automation'")||!serverAutomation.includes('applyServerChanges'))fail('v17.7.0 server automation client missing');else ok('v17.7.0 server automation client present'); if(!performance.includes('ux-fixes-v1764.js')||!performance.includes('SunUXFixV1764'))fail('UX fix loader missing');else ok('UX fix loader present'); if(!performance.includes('hotfix-v1763.js')||!performance.includes('SunHotfixV1763'))fail('v17.6.3 hotfix loader missing');else ok('v17.6.3 hotfix loader present'); if(!performance.includes('ops-ux-v1762.js')||!performance.includes('SunOpsUXV1762'))fail('ops UX loader missing');else ok('ops UX loader present'); for(const marker of ['patchDeveloperOpen','enhanceDeveloperGate','data-saas-admin','stopImmediatePropagation','instanceof HTMLElement']) if(!hotfix.includes(marker))fail(`developer/SaaS hotfix marker missing: ${marker}`);else ok(`developer/SaaS hotfix marker: ${marker}`); for(const marker of ['SUPPORT_POLL_MS=20000','supportReadPermission','refreshSupportWorkspace','sun-menu-editor-v1762','showCalendarDay',"ROUTE_BASE_KEY='sunRouteBaseV1'",'showRouteOrder','routeOpenYandex']) if(!ops.includes(marker)) fail(`ops UX marker missing: ${marker}`); else ok(`ops UX marker: ${marker}`); for(const marker of ["AUTO_DELAY_MS=60*1000","order.prepayment=total","order.status='Отдан заказчику'",'sunAutoCompletedAt','classificationDate','persistOfferTemplate','clientOfferTemplateId','offerTemplateId','sun-v1764-menu-icon','CateriumServerAutomationV1770?.enabled']) if(!ux.includes(marker))fail(`UX compatibility marker missing: ${marker}`);else ok(`UX compatibility marker: ${marker}`); for(const marker of ['CLOUD_RPC_TIMEOUT_MS=12000','CLOUD_CONFLICT_MAX_RETRIES=4','refreshSupportWorkspace',"const VERSION = '17.7.3'",'ERROR_DEDUPE_MS=5*60*1000','DEV_ADMIN_TTL_MS=30000']) if(!runtime.includes(marker))fail(`stability marker missing: ${marker}`);else ok(`stability marker: ${marker}`); if(!hotfix.includes('checkPlatformAdmin?.(false)')||!hotfix.includes('},10000);'))fail('Developer fallback polling is still aggressive');else ok('Developer fallback polling is throttled'); if(!ux.includes('sunMenuIconV1766')||!ux.includes('sun-offer-template-mini-editorial-grid')||!runtime.includes('explicitTemplate'))fail('v17.6.6 proposal/menu markers missing');else ok('v17.6.6 proposal/menu markers present'); if(!classic.includes("const VERSION='17.6.7'")||!classic.includes('CLASSIC_IDS')||!classic.includes('ARCHIVE_IDS')||!classic.includes('renderPages'))fail('v17.6.7 classic PDF module missing');else ok('v17.6.7 classic PDF module present'); if(!developerUX.includes("const VERSION='17.6.8'")||!developerUX.includes('sun_dev_delete_company_v1768')||!developerUX.includes('sun_dev_error_groups_v1768')||!developerUX.includes('selectedAccountsWorkspace')||!developerUX.includes('sun-dev-plan-matrix')||!developerUX.includes('KNOWN_DOM_RACE'))fail('v17.6.8 Developer Console UX module missing');else ok('v17.6.8 Developer Console UX module present'); if(!offerWorkspace.includes("const VERSION='17.6.9'")||!offerWorkspace.includes('data-offer-workspace-tab=\"templates\"')||!offerWorkspace.includes('data-offer-gallery-slot')||!offerWorkspace.includes('sunOfferGalleryOverridesV1769'))fail('v17.6.9 offer workspace module missing');else ok('v17.6.9 offer workspace module present'); if(/service_role\s*[:=]\s*['"][A-Za-z0-9._-]{20,}/i.test(ops+hotfix+ux+classic+developerUX+offerWorkspace+dataLayer+serverAutomation)||/eyJ[a-zA-Z0-9_-]{30,}/.test(ops+hotfix+ux+classic+developerUX+offerWorkspace+dataLayer+serverAutomation))fail('possible secret in client modules');else ok('client modules have no hard-coded secret'); if(process.exitCode)process.exit(process.exitCode);