'use strict'; function deploymentBase(value){ const raw=String(value||'').trim(); if(!raw)return null; const base=new URL(raw.endsWith('/')?raw:`${raw}/`); if(!['http:','https:'].includes(base.protocol))throw new Error('LinkField public URL must use HTTP or HTTPS'); return base; } async function fetchChecked(url,{accept='application/json',timeoutMs=10_000}={}){ const response=await fetch(url,{headers:{accept},signal:AbortSignal.timeout(timeoutMs)}); return response; } async function checkPublicDeployment(publicUrl,{appVersion,timeoutMs=10_000}={}){ const base=deploymentBase(publicUrl);if(!base)return null; const pageUrl=new URL('',base),apiUrl=new URL('api/cloud/status',base),bridgeUrl=new URL('api-bridge.php?path=/api/cloud/status',base); const page=await fetchChecked(pageUrl,{accept:'text/html',timeoutMs}),pageBody=await page.text(); if(!page.ok||!pageBody.includes('LinkField'))throw new Error(`Public page failed at ${pageUrl.href}: HTTP ${page.status}`); const disclosed=page.headers.get('server')||'';if(/\//.test(disclosed))throw new Error(`Public server discloses a detailed version: ${disclosed}`); for(const relative of ['server.js','package.json','scripts/service-control.js','.linkfield-deployment.json']){ const url=new URL(relative,base),response=await fetchChecked(url,{accept:'text/plain',timeoutMs}); if(![403,404].includes(response.status))throw new Error(`Private deployment file is public at ${url.href}: HTTP ${response.status}`); await response.body?.cancel().catch(()=>{}); } const results=[]; for(const [kind,url] of [['API',apiUrl],['PHP bridge',bridgeUrl]]){ const response=await fetchChecked(url,{timeoutMs}),body=await response.json().catch(()=>({})); if(!response.ok||body?.available!==true||body?.appVersion!==appVersion)throw new Error(`Public ${kind} failed at ${url.href}: HTTP ${response.status}`); if(kind==='PHP bridge'&&response.headers.get('x-linkfield-bridge')!=='php')throw new Error(`Public PHP bridge did not identify itself at ${url.href}`); results.push({kind,url:url.href,status:response.status}); } return{base:base.href,page:pageUrl.href,api:results[0].url,bridge:results[1].url}; } module.exports=Object.freeze({deploymentBase,checkPublicDeployment});