@smart-cloud/publisher-exporter 1.1.63 → 1.1.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import {S3Client,HeadObjectCommand,CopyObjectCommand,DeleteObjectsCommand,PutObjectCommand,GetObjectCommand}from'@aws-sdk/client-s3';import {chromium}from'playwright';import {createHash}from'crypto';import {lookup}from'dns/promises';import {isIP}from'net';import'fast-glob';import'fs/promises';import'os';import T from'path';import'worker_threads';var b=1,ne=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;function v(e,t){let r=String(e??"").trim();if(!ne.test(r))throw new Error(`${t} must match ${ne.source} and be at most 128 characters.`);return r}function C(e){let t=String(e??"").trim().replace(/^\/+|\/+$/g,"");if(!t)return "";let r=t.split("/").filter(Boolean);if(r.some(n=>n==="."||n===".."))throw new Error("S3 prefix contains an unsafe dot path segment.");return `${r.join("/")}/`}function E(e,...t){let r=C(e),n=t.map((o,s)=>{let i=String(o??"").replace(/^\/+|\/+$/g,"");if(!i||i.split("/").some(c=>c==="."||c===".."))throw new Error(`Unsafe S3 key segment at index ${s}.`);return i});return `${r}${n.join("/")}`}function z(e,t,r){return `${E(e,"jobs",v(t,"jobId"),"tasks",v(r,"taskId"))}/`}function M(e){return createHash("sha256").update(e).digest("hex")}function k(e,t){let r=String(e??"").replace(/^\/+/,""),n=C(t);if(!r||r.includes("\0"))throw new Error("S3 object key is empty or invalid.");if(n&&!r.startsWith(n))throw new Error(`S3 object key is outside the configured workspace prefix: ${r}`);if(r.split("/").some(o=>o===".."))throw new Error("S3 object key contains an unsafe parent segment.");return r}function O(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new Error(`${t} must be a JSON object.`);return e}function m(e,t){if(typeof e!="string"||!e.trim())throw new Error(`${t} must be a non-empty string.`);return e.trim()}function R(e,t,r,n){if(!Number.isInteger(e)||Number(e)<r||Number(e)>n)throw new Error(`${t} must be an integer between ${r} and ${n}.`);return Number(e)}function xe(e){return e.hostname.replace(/^\[|\]$/g,"").replace(/\.$/,"").toLowerCase()}function A(e){let t=e.split(".");if(t.length!==4)return null;let r=t.map(n=>Number(n));return r.every((n,o)=>Number.isInteger(n)&&n>=0&&n<=255&&String(n)===t[o])?r:null}function J(e){let t=e.toLowerCase();if(t.includes("%")||t.split("::").length>2)return null;let r=t,n=r.lastIndexOf(":");if(r.includes(".")&&n>=0){let l=A(r.slice(n+1));if(!l)return null;r=`${r.slice(0,n)}:${((l[0]??0)*256+(l[1]??0)).toString(16)}:${((l[2]??0)*256+(l[3]??0)).toString(16)}`;}let[o,s]=r.split("::"),i=o?o.split(":"):[],c=s?s.split(":"):[],u=r.includes("::"),a=8-i.length-c.length;return u&&a<1||!u&&a!==0||[...i,...c].some(l=>!/^[0-9a-f]{1,4}$/.test(l))?null:[...i.map(l=>Number.parseInt(l,16)),...Array.from({length:a},()=>0),...c.map(l=>Number.parseInt(l,16))]}function oe(e){let t=A(e);if(!t)return "an invalid IPv4 address";let[r=0,n=0,o=0,s=0]=t;return r===0?"the unspecified/current-network IPv4 range":r===10||r===172&&n>=16&&n<=31||r===192&&n===168?"an RFC1918 private IPv4 range":r===127?"the IPv4 loopback range":r===169&&n===254?"the IPv4 link-local/metadata range":r===100&&n===100&&o===100&&s===200?"a cloud instance metadata endpoint":r===100&&n>=64&&n<=127?"the shared carrier-grade NAT IPv4 range":r===198&&(n===18||n===19)?"the IPv4 benchmarking range":r>=224&&r<=239?"the IPv4 multicast range":r>=240?"the reserved IPv4 range":null}function Se(e){let t=J(e);if(!t||t.length!==8)return "an invalid IPv6 address";if(t.every(o=>o===0))return "the unspecified IPv6 address";if(t.slice(0,7).every(o=>o===0)&&t[7]===1)return "the IPv6 loopback address";let r=t[0]??0;if((r&65024)===64512)return "an IPv6 unique-local range";if((r&65472)===65152)return "the IPv6 link-local range";if((r&65280)===65280)return "the IPv6 multicast range";if(t.slice(0,5).every(o=>o===0)&&t[5]===65535){let o=t[6]??0,s=t[7]??0;return oe(`${o>>8}.${o&255}.${s>>8}.${s&255}`)}return null}function G(e){return A(e)?oe(e):J(e)?Se(e):"an invalid IP address"}function V(e,t){let r=m(e,t),n;try{n=new URL(r);}catch{throw new Error(`${t} must be a valid URL.`)}if(n.protocol!=="http:"&&n.protocol!=="https:")throw new Error(`${t} must use HTTP or HTTPS.`);if(n.username||n.password)throw new Error(`${t} must not contain URL credentials.`);let o=xe(n);if(!o)throw new Error(`${t} must contain a hostname.`);if(o==="localhost"||o.endsWith(".localhost"))throw new Error(`${t} must not target localhost.`);if(o==="metadata.google.internal")throw new Error(`${t} must not target a cloud instance metadata endpoint.`);if(A(o)||J(o)){let s=G(o);if(s)throw new Error(`${t} must not target ${s}.`)}return n}function Pe(e){let t=V(e.sourceOrigin,"sourceOrigin").origin;if(!Array.isArray(e.urls)||e.urls.length<1||e.urls.length>20)throw new Error("Render tasks require between 1 and 20 URLs.");for(let[s,i]of e.urls.entries())if(V(i,`urls[${s}]`).origin!==t)throw new Error(`urls[${s}] is outside sourceOrigin.`);let r=O(e.options,"options");if(typeof r.ignoreHttpsErrors!="boolean")throw new Error("options.ignoreHttpsErrors must be a boolean.");if(typeof r.javaScriptEnabled!="boolean")throw new Error("options.javaScriptEnabled must be a boolean.");let n=O(r.viewport,"options.viewport");R(n.width,"options.viewport.width",320,7680),R(n.height,"options.viewport.height",240,7680),R(r.navigationTimeoutMs,"options.navigationTimeoutMs",1e3,6e5),R(r.autoScrollTimeoutMs,"options.autoScrollTimeoutMs",0,12e4);let o=O(r.readiness,"options.readiness");for(let s of ["waitForSelector","waitForFunction"])if(o[s]!==null&&typeof o[s]!="string")throw new Error(`options.readiness.${s} must be a string or null.`);R(o.timeoutMs,"options.readiness.timeoutMs",0,12e4),R(o.fallbackWaitMs,"options.readiness.fallbackWaitMs",0,12e4);}function $e(e){if(m(e.configKey,"configKey"),m(e.assetMapKey,"assetMapKey"),e.previousAssetMapKey!==void 0&&m(e.previousAssetMapKey,"previousAssetMapKey"),!Array.isArray(e.items)||e.items.length<1||e.items.length>500)throw new Error("Rewrite tasks require between 1 and 500 objects.");for(let[t,r]of e.items.entries()){let n=O(r,`items[${t}]`);m(n.inputKey,`items[${t}].inputKey`),m(n.outputKey,`items[${t}].outputKey`),m(n.relativePath,`items[${t}].relativePath`);}}function Ie(e){if(v(m(e.targetId,"targetId"),"targetId"),!Array.isArray(e.items)||e.items.length>1e3)throw new Error("Deploy copy tasks accept at most 1000 objects.");for(let[r,n]of e.items.entries()){let o=O(n,`items[${r}]`);m(o.sourceKey,`items[${r}].sourceKey`),m(o.targetRelativeKey,`items[${r}].targetRelativeKey`),m(o.sha256,`items[${r}].sha256`),o.normalizedSha256!==void 0&&m(o.normalizedSha256,`items[${r}].normalizedSha256`),R(o.bytes,`items[${r}].bytes`,0,Number.MAX_SAFE_INTEGER),m(o.contentType,`items[${r}].contentType`),m(o.cacheControl,`items[${r}].cacheControl`);}let t=e.deleteRelativeKeys??[];if(!Array.isArray(t)||t.length>1e3)throw new Error("Deploy copy tasks accept at most 1000 deletion keys.");for(let[r,n]of t.entries())m(n,`deleteRelativeKeys[${r}]`);if(e.items.length===0&&t.length===0)throw new Error("Deploy copy tasks require copy or deletion work.")}function se(e){if(!e||typeof e!="object")throw new Error("Worker event must be a JSON object.");let t=e;if(t.schemaVersion!==b)throw new Error(`Unsupported worker protocol version: ${String(t.schemaVersion)}.`);let r=t.operation;if(r!=="render"&&r!=="rewrite"&&r!=="deploy-copy"&&r!=="health")throw new Error(`Unsupported worker operation: ${String(r)}.`);return v(String(t.jobId??""),"jobId"),v(String(t.taskId??""),"taskId"),r==="render"&&Pe(t),r==="rewrite"&&$e(t),r==="deploy-copy"&&Ie(t),e}function Ce(e){return e.hostname.replace(/^\[|\]$/g,"").replace(/\.$/,"").toLowerCase()}async function Te(e){return (await lookup(e,{all:true,verbatim:true})).map(t=>t.address)}async function We(e,t,r=Te){let n=V(e,t),o=Ce(n);if(isIP(o))return n;let s;try{s=await r(o);}catch{throw new Error(`${t} hostname could not be resolved safely.`)}if(s.length===0)throw new Error(`${t} hostname resolved to no addresses.`);for(let i of s){let c=G(i);if(c)throw new Error(`${t} hostname resolves to ${c}.`)}return n}async function ie(e,t=We){let r=null;return await e.route("**/*",async n=>{try{await t(n.request().url(),"render request");}catch(o){r??=o instanceof Error?o:new Error(String(o)),await n.abort("blockedbyclient").catch(()=>{});return}await n.continue();}),await e.routeWebSocket("**/*",async n=>{try{await t(n.url(),"render WebSocket request");}catch(o){r??=o instanceof Error?o:new Error(String(o)),await n.close({code:1008,reason:"Blocked by network policy"});return}await n.close({code:1008,reason:"Only HTTP(S) requests are allowed"}),r??=new Error("render WebSocket request must use HTTP or HTTPS.");}),{getViolation:()=>r}}function le(e){return e.replace(/&quot;/g,'"').replace(/&#34;/g,'"').replace(/&apos;/g,"'").replace(/&#39;/g,"'").replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">")}function x(e){return e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/'/g,"&#39;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function U(e){return e.replace(/\//g,"\\/")}function ae(e){return e.replace(/\//g,"\\\\/")}function je(e){try{let t=new URL(e);if(t.protocol!=="http:"&&t.protocol!=="https:")return null;let r=t.pathname==="/"&&!t.search&&!t.hash?"":`${t.pathname}${t.search}${t.hash}`;return `//${t.host}${r}`}catch{return null}}function ce(e,t,r){if(!t)return;e[t]=r;let n=U(t),o=U(r);e[n]=o;let s=ae(t),i=ae(r);e[s]=i;let c=x(t),u=x(r);e[c]=u;let a=x(n),l=x(o);e[a]=l;let p=x(s),d=x(i);e[p]=d;}function L(e,t,r){ce(e,t,r);let n=je(t);n&&n!==t&&ce(e,n,r);}function ue(e,t){try{return new URL(e,t==="."?"https://relative.invalid":t).pathname}catch{return e.startsWith("/")?e:`/${e.replace(/^\.\//,"")}`}}function pe(e,t,r){if(!t)return r;let n=T.dirname(T.resolve(t)),o=T.resolve(e,r.replace(/^\/+/,"")),s=T.relative(n,o).replace(/\\/g,"/");return s?(s.startsWith(".")||(s=`./${s}`),s):"."}var Ve=["wp-content/","wp-includes/","wp-admin/","wp-json/","_next/"],Me=new Set([".html",".htm"]),de="WPSuite.io Static Publisher",Ae=de.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");function Ke(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Ue(e){return e.startsWith("//")||e.startsWith("\\/\\/")||e.startsWith("\\\\/\\\\/")}function Le(e,t,r){return Ue(t)?e.replace(new RegExp(`(?<!:)${Ke(t)}`,"g"),r):e.split(t).join(r)}function _e(e,t,r){if(e.wpsuite?.siteSettings?.subscriber===true||e.wpsuite?.subscriptionType==="PROFESSIONAL"||e.wpsuite?.subscriptionType==="AGENCY"||!r)return false;let o=T.extname(r).toLowerCase();return Me.has(o)?/<head\b|<html\b|<!doctype html/i.test(t):false}function Fe(e){if(new RegExp(`<meta\\b(?=[^>]*\\bname=(["'])generator\\1)(?=[^>]*\\bcontent=(["'])${Ae}\\2)[^>]*\\/?>`,"i").test(e))return e;let t=`<meta name="generator" content="${de}" />`;if(e.match(/(\r?\n)([ \t]*)<\/head>/i))return e.replace(/(\r?\n)([ \t]*)<\/head>/i,`$1$2${t}$1$2</head>`);let n=e.includes(`\r
2
2
  `)?`\r
3
3
  `:`
4
- `;return e.replace(/<head\b[^>]*>/i,o=>`${o}${n} ${t}`)}function Ne(e){let t=le(e).trim();return t.startsWith("{")||t.startsWith("[")||t.includes("\\/")||/"@context"|"@type"/.test(t)}function He(e,t){let r=[...new Set(t)].filter(s=>s.includes("/")&&!/\\+\//.test(s)).map(s=>[s,U(s)]).filter(([s,i])=>s!==i).sort((s,i)=>i[0].length-s[0].length);if(!r.length)return e;let n=s=>{let i=s;for(let[c,u]of r)i=i.split(c).join(u);return i},o=e.replace(/(<script\b[^>]*\btype=["']application\/(?:ld\+)?json["'][^>]*>)([\s\S]*?)(<\/script>)/gi,(s,i,c,u)=>`${i}${n(c)}${u}`);return o=o.replace(/(<meta\b[^>]*\bcontent=(['"]))([\s\S]*?)(\2[^>]*>)/gi,(s,i,c,u,a)=>Ne(u)?`${i}${n(u)}${a}`:s),o}function Be(e,t){if(!t)return null;let r=T.dirname(T.resolve(t)),n=T.resolve(e.outputDir),o=T.relative(r,n).replace(/\\/g,"/");return o?(o.startsWith(".")||(o=`./${o}`),o.endsWith("/")?o:`${o}/`):"./"}function De(e,t,r){let n=Be(t,r);if(!n)return e;let o=e;for(let s of Ve){let i=s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),c=new RegExp(`(?:\\.{1,}/)+${i}`,"g"),u=new RegExp(`(?<!\\.)/${i}`,"g");o=o.replace(c,`${n}${s}`).replace(u,`${n}${s}`);let a=s.replace(/\//g,"\\/"),l=n.replace(/\//g,"\\/"),p=a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),d=new RegExp(`(?:\\.{1,}\\\\/)+${p}`,"g"),P=new RegExp(`(?:\\.{1,}(?:\\\\/|\\\\))+${p}`,"g"),$=new RegExp(`(?<![\\.\\\\])\\/${p}`,"g");o=o.replace(d,`${l}${a}`).replace(P,`${l}${a}`).replace($,`${l}${a}`);}return o}function Y(e,t,r){if(e.urlRewriteMode==="absolute")return t;let n=(()=>{try{return new URL(t,e.targetOrigin==="."?"https://relative.invalid":e.targetOrigin)}catch{return null}})(),o=n?n.pathname:ue(t,e.targetOrigin),s=n?`${n.search}${n.hash}`:"";return e.urlRewriteMode==="root-relative"?`${o}${s}`:`${pe(e.outputDir,r,o)}${s}`}function X(e,t,r,n,o){L(e,r,Y(t,n,o));}function qe(e,t,r,n,o){if(!r||r===n)return;L(e,r,n);let s=Y(t,r,o),i=Y(t,n,o);L(e,s,i);}function ge(e,t,r,n,o={}){let s=e,i={};X(i,t,t.sourceOrigin,t.targetOrigin,n);for(let[u,a]of Object.entries(t.extraReplacements))X(i,t,u,a,n);for(let[u,a]of Object.entries(r))X(i,t,u,a,n);for(let[u,a]of Object.entries(o)){let l=r[u];!l||l===a||qe(i,t,a,l,n);}let c=Object.entries(i).sort((u,a)=>a[0].length-u[0].length);for(let[u,a]of c)s=Le(s,u,a);return t.urlRewriteMode!=="absolute"&&(s=De(s,t,n)),s=He(s,c.map(([,u])=>u)),_e(t,s,n)&&(s=Fe(s)),s}var S=String(process.env.PUBLISHER_WORKSPACE_BUCKET??"").trim(),w=C(process.env.PUBLISHER_WORKSPACE_PREFIX??""),_=String(process.env.PUBLISHER_ALLOWED_OPERATION??"").trim(),H=String(process.env.PUBLISHER_EXPORTER_VERSION??"unknown").trim(),Z=String(process.env.PUBLISHER_PROXY_URL??"").trim(),Qe=et(process.env.PUBLISHER_ALLOWED_TARGETS??"[]"),we=new S3Client({}),me=new Map,h=null;function et(e){try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(r=>{if(!r||typeof r!="object")return !1;let n=r;return ["id","bucketName","prefix","region"].every(o=>typeof n[o]=="string")}).map(r=>({...r,prefix:C(r.prefix)})):[]}catch{return []}}function Q(){if(!S)throw new Error("PUBLISHER_WORKSPACE_BUCKET is required.")}async function tt(){if(h){let t=await h.catch(()=>null);if(t?.isConnected())return t}let e=chromium.launch({headless:true,args:["--no-sandbox","--disable-setuid-sandbox","--disable-dev-shm-usage","--no-zygote","--single-process","--disable-gpu"],...Z?{proxy:{server:Z}}:{}});h=e;try{let t=await e;return t.once("disconnected",()=>{h===e&&(h=null);}),t}catch(t){throw h===e&&(h=null),t}}async function rt(e){let t;for(let r=1;r<=2;r+=1){let n=await tt(),o=null;try{o=await n.newContext({viewport:e.options.viewport,ignoreHTTPSErrors:e.options.ignoreHttpsErrors,javaScriptEnabled:e.options.javaScriptEnabled,serviceWorkers:"block",userAgent:"WPSuiteStaticPublisher/remote-worker-v1"}),e.options.javaScriptEnabled&&await nt(o);let s=await ie(o);return {context:o,page:await o.newPage(),networkPolicyViolation:s.getViolation}}catch(s){t=s,await o?.close().catch(()=>{}),h=null,await n.close().catch(()=>{});}}throw t instanceof Error?t:new Error(`Unable to create a Chromium page: ${String(t)}`)}async function nt(e){await e.addInitScript(()=>{Object.defineProperty(window,"__WPSUITE_STATIC_EXPORT__",{value:true,writable:false,configurable:true});let t=r=>{if(r==null||String(r)==="")return true;try{let n=new URL(String(r),window.location.href),o=new URL(window.location.href);return n.hash="",o.hash="",n.href===o.href}catch{return false}};try{let r=window.location.assign.bind(window.location),n=window.location.replace.bind(window.location);Object.defineProperty(window.location,"assign",{configurable:!0,value:o=>{if(!t(o))return r(o)}}),Object.defineProperty(window.location,"replace",{configurable:!0,value:o=>{if(!t(o))return n(o)}}),Object.defineProperty(window.location,"reload",{configurable:!0,value:()=>{}});}catch{}});}async function ot(e,t){await e.evaluate(async r=>{await new Promise(n=>{let o=0,s=700,i=Date.now(),c=()=>{window.clearInterval(u),window.scrollTo(0,0),n();},u=window.setInterval(()=>{if(Date.now()-i>=r){c();return}window.scrollBy(0,s),o+=s;let a=Math.max(document.body?.scrollHeight||0,document.documentElement?.scrollHeight||0);o>=a+window.innerHeight&&c();},120);});},t);}async function ye(e){Q();let t=k(e,w),r=await we.send(new GetObjectCommand({Bucket:S,Key:t}));if(!r.Body)throw new Error(`S3 object has no body: ${t}`);return await r.Body.transformToString("utf8")}async function N(e){return JSON.parse(await ye(e))}async function ee(e,t,r){Q();let n=k(e,w),o=typeof t=="string"?Buffer.from(t,"utf8"):t,s=await we.send(new PutObjectCommand({Bucket:S,Key:n,Body:o,ContentType:r,Metadata:{sha256:M(o)}}));return {bucket:S,key:n,sha256:M(o),bytes:o.byteLength,contentType:r,...s.ETag?{etag:s.ETag}:{},...s.VersionId?{versionId:s.VersionId}:{}}}async function st(e,t){return await ee(e,`${JSON.stringify(t,null,2)}
5
- `,"application/json; charset=utf-8")}async function it(e){try{let t=await N(e);return t.response??t}catch(t){if(t.name==="NoSuchKey"||t.$metadata?.httpStatusCode===404)return null;throw t}}function F(e){return [...new Set(e)].filter(t=>{try{let r=new URL(t);return (r.protocol==="http:"||r.protocol==="https:")&&!r.username&&!r.password}catch{return false}}).sort()}async function at(e,t,r){let n=null,o=null,s,i=new Set;try{let c=await rt(e);n=c.context,o=c.page,s=c.networkPolicyViolation,o.on("request",g=>i.add(g.url()));let u=await o.goto(t,{waitUntil:"domcontentloaded",timeout:e.options.navigationTimeoutMs});e.options.readiness.waitForSelector&&await o.waitForSelector(e.options.readiness.waitForSelector,{timeout:e.options.readiness.timeoutMs}).catch(()=>{}),e.options.javaScriptEnabled&&e.options.readiness.waitForFunction&&await o.waitForFunction(e.options.readiness.waitForFunction,void 0,{timeout:e.options.readiness.timeoutMs}).catch(()=>{}),e.options.javaScriptEnabled?await ot(o,e.options.autoScrollTimeoutMs):await o.waitForLoadState("load",{timeout:e.options.readiness.timeoutMs}).catch(()=>{}),e.options.readiness.fallbackWaitMs>0&&await o.waitForTimeout(e.options.readiness.fallbackWaitMs);let a=s?.();if(a)throw a;let l=await o.evaluate(()=>{let g=(y,Re)=>{let D=[];for(let be of document.querySelectorAll(y))for(let te of Re){let q=be.getAttribute(te);if(q)if(te.includes("srcset"))for(let Ee of q.split(",")){let re=Ee.trim().split(/\s+/,1)[0];re&&D.push(re);}else D.push(q);}return D},I=y=>{try{return new URL(y,document.baseURI).href}catch{return null}};return {links:g("a[href]",["href"]).map(I).filter(y=>y!==null),assets:g("img,script,link,source,video,audio,iframe,[data-src],[data-href],[data-resource-url]",["src","href","srcset","data-src","data-srcset","data-href","data-resource-url","poster"]).map(I).filter(y=>y!==null)}}),p=await o.content(),d=s();if(d)throw d;let P=new URL(t),$=F(l.links).filter(g=>{let I=new URL(g);return I.hash="",I.origin===P.origin}),B=E(z(w,e.jobId,e.taskId),"pages",`${String(r).padStart(4,"0")}-${M(t).slice(0,24)}.html`),W=await ee(B,p,"text/html; charset=utf-8"),f=(await n.cookies()).map(g=>({name:g.name,domain:g.domain,path:g.path,expires:g.expires,httpOnly:g.httpOnly,secure:g.secure,sameSite:g.sameSite,..."partitionKey"in g?{partitioned:!0}:{}})),j=u?.status()??null;return {requestedUrl:t,finalUrl:o?.url()||null,outcome:j!==null&&j>=400?"http-error":"rendered",httpStatus:j,contentType:u?.headers()["content-type"]??null,html:W,discoveredPages:$,discoveredAssets:F(l.assets),networkUrls:F([...i]),cookies:f}}catch(c){return {requestedUrl:t,finalUrl:o?.url()||null,outcome:"failed",httpStatus:null,contentType:null,html:null,discoveredPages:[],discoveredAssets:[],networkUrls:F([...i]),cookies:[],error:c instanceof Error?c.message:String(c)}}finally{await n?.close().catch(()=>{});}}async function ct(e,t){if(!Array.isArray(e.urls)||e.urls.length<1||e.urls.length>20)throw new Error("Render tasks require between 1 and 20 URLs.");let r=new URL(e.sourceOrigin).origin,n=[];for(let s=0;s<e.urls.length;s+=1){let i=e.urls[s];if(!i)continue;let c=t.getRemainingTimeInMillis?.();if(c!==void 0&&c<6e4){n.push({requestedUrl:i,finalUrl:null,outcome:"deferred",httpStatus:null,contentType:null,html:null,discoveredPages:[],discoveredAssets:[],networkUrls:[],cookies:[],error:"Worker stopped accepting URLs because less than 60 seconds remained."});continue}let u=new URL(i);if(u.origin!==r)throw new Error(`Primary render URL is outside sourceOrigin: ${i}`);n.push(await at(e,u.toString(),s));}let o=n.filter(s=>s.outcome==="failed"||s.outcome==="deferred").length;return {response:{schemaVersion:b,operation:"render",jobId:e.jobId,taskId:e.taskId,status:o>0?"partial":"succeeded",completedItems:n.length-o,failedItems:o,exporterVersion:H},detail:{pages:n}}}async function lt(e){if(!Array.isArray(e.items)||e.items.length<1||e.items.length>500)throw new Error("Rewrite tasks require between 1 and 500 objects.");let r={...await N(e.configKey),outputDir:"/workspace"},n=await N(e.assetMapKey),o=e.previousAssetMapKey?await N(e.previousAssetMapKey):{},s=[];for(let i of e.items){let c=k(i.inputKey,w),u=k(i.outputKey,w),a=await ye(c),l=`/workspace/${i.relativePath.replace(/^\/+/,"")}`,p=ge(a,r,n,l,o),d=await ee(u,p,"text/plain; charset=utf-8");s.push({inputKey:c,outputKey:u,changed:p!==a,output:d});}return {response:{schemaVersion:b,operation:"rewrite",jobId:e.jobId,taskId:e.taskId,status:"succeeded",completedItems:s.length,failedItems:0,exporterVersion:H},detail:{objects:s}}}function ut(e,t){return `${e}/${t.split("/").map(r=>encodeURIComponent(r)).join("/")}`}async function pt(e){if(!Array.isArray(e.items)||e.items.length>1e3)throw new Error("Deploy copy tasks accept at most 1000 objects.");let t=Qe.find(a=>a.id===e.targetId);if(!t)throw new Error(`Unknown deployment target: ${e.targetId}`);let r=me.get(t.region);r||(r=new S3Client({region:t.region}),me.set(t.region,r));let n=[];for(let a of e.items){let l=k(a.sourceKey,w),p=E(t.prefix,a.targetRelativeKey);try{let d=await r.send(new HeadObjectCommand({Bucket:t.bucketName,Key:p})).catch(f=>{if((f&&typeof f=="object"&&"$metadata"in f?Number(f.$metadata?.httpStatusCode):0)===404||f?.name==="NotFound")return null;throw f}),P=String(d?.CacheControl??"").replace(/\s+/g,"")===a.cacheControl.replace(/\s+/g,""),$=d?.Metadata?.["wpsuite-sha256"]===a.sha256,B=!!a.normalizedSha256&&d?.Metadata?.["wpsuite-normalized-sha256"]===a.normalizedSha256;if(P&&($||B)){n.push({sourceKey:l,targetKey:p,status:"skipped"});continue}let W=await r.send(new CopyObjectCommand({Bucket:t.bucketName,Key:p,CopySource:ut(S,l),MetadataDirective:"REPLACE",ContentType:a.contentType,CacheControl:a.cacheControl,Metadata:{"wpsuite-sha256":a.sha256,...a.normalizedSha256?{"wpsuite-normalized-sha256":a.normalizedSha256}:{}}}));n.push({sourceKey:l,targetKey:p,status:"copied",...W.CopyObjectResult?.ETag?{etag:W.CopyObjectResult.ETag}:{}});}catch(d){n.push({sourceKey:l,targetKey:p,status:"failed",error:d instanceof Error?d.message:String(d)});}}let s=(e.deleteRelativeKeys??[]).map(a=>E(t.prefix,a)),i=[],c=[];if(s.length>0){let a=await r.send(new DeleteObjectsCommand({Bucket:t.bucketName,Delete:{Objects:s.map(l=>({Key:l})),Quiet:false}}));i.push(...(a.Deleted??[]).flatMap(l=>l.Key?[l.Key]:[])),c.push(...(a.Errors??[]).map(l=>({key:l.Key??"",error:l.Message??l.Code??"S3 deletion failed"})));}let u=n.filter(a=>a.status==="failed").length+c.length;return {response:{schemaVersion:b,operation:"deploy-copy",jobId:e.jobId,taskId:e.taskId,status:u>0?"partial":"succeeded",completedItems:n.filter(a=>a.status!=="failed").length+i.length,failedItems:u,exporterVersion:H},detail:{objects:n,deletedKeys:i,deleteErrors:c,targetId:t.id}}}async function fe(e,t){if(e.operation==="health")return {response:{schemaVersion:b,operation:"health",jobId:e.jobId,taskId:e.taskId,status:"succeeded",completedItems:1,failedItems:0,exporterVersion:H},detail:{allowedOperation:_,workspaceBucket:S,workspacePrefix:w,proxyConfigured:!!Z}};if(_&&_!==e.operation)throw new Error(`Function is configured for ${_}, not ${e.operation}.`);return e.operation==="render"?await ct(e,t):e.operation==="rewrite"?await lt(e):await pt(e)}async function Nt(e,t={}){let r=se(e);if(r.operation==="health")return (await fe(r,t)).response;Q();let n=E(z(w,r.jobId,r.taskId),"result.json"),o=await it(n);if(o)return o;let s=await fe(r,t),i={...s.response,resultKey:n};return await st(n,{schemaVersion:b,response:i,detail:s.detail,completedAt:new Date().toISOString(),requestId:t.awsRequestId??null}),i}export{Nt as handler};
4
+ `;return e.replace(/<head\b[^>]*>/i,o=>`${o}${n} ${t}`)}function Ne(e){let t=le(e).trim();return t.startsWith("{")||t.startsWith("[")||t.includes("\\/")||/"@context"|"@type"/.test(t)}function He(e,t){let r=[...new Set(t)].filter(s=>s.includes("/")&&!/\\+\//.test(s)).map(s=>[s,U(s)]).filter(([s,i])=>s!==i).sort((s,i)=>i[0].length-s[0].length);if(!r.length)return e;let n=s=>{let i=s;for(let[c,u]of r)i=i.split(c).join(u);return i},o=e.replace(/(<script\b[^>]*\btype=["']application\/(?:ld\+)?json["'][^>]*>)([\s\S]*?)(<\/script>)/gi,(s,i,c,u)=>`${i}${n(c)}${u}`);return o=o.replace(/(<meta\b[^>]*\bcontent=(['"]))([\s\S]*?)(\2[^>]*>)/gi,(s,i,c,u,a)=>Ne(u)?`${i}${n(u)}${a}`:s),o}function Be(e,t){if(!t)return null;let r=T.dirname(T.resolve(t)),n=T.resolve(e.outputDir),o=T.relative(r,n).replace(/\\/g,"/");return o?(o.startsWith(".")||(o=`./${o}`),o.endsWith("/")?o:`${o}/`):"./"}function De(e,t,r){let n=Be(t,r);if(!n)return e;let o=e;for(let s of Ve){let i=s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),c=new RegExp(`(?:\\.{1,}/)+${i}`,"g"),u=new RegExp(`(?<!\\.)/${i}`,"g");o=o.replace(c,`${n}${s}`).replace(u,`${n}${s}`);let a=s.replace(/\//g,"\\/"),l=n.replace(/\//g,"\\/"),p=a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),d=new RegExp(`(?:\\.{1,}\\\\/)+${p}`,"g"),P=new RegExp(`(?:\\.{1,}(?:\\\\/|\\\\))+${p}`,"g"),$=new RegExp(`(?<![\\.\\\\])\\/${p}`,"g");o=o.replace(d,`${l}${a}`).replace(P,`${l}${a}`).replace($,`${l}${a}`);}return o}function Y(e,t,r){if(e.urlRewriteMode==="absolute")return t;let n=(()=>{try{return new URL(t,e.targetOrigin==="."?"https://relative.invalid":e.targetOrigin)}catch{return null}})(),o=n?n.pathname:ue(t,e.targetOrigin),s=n?`${n.search}${n.hash}`:"";return e.urlRewriteMode==="root-relative"?`${o}${s}`:`${pe(e.outputDir,r,o)}${s}`}function X(e,t,r,n,o){L(e,r,Y(t,n,o));}function qe(e,t,r,n,o){if(!r||r===n)return;L(e,r,n);let s=Y(t,r,o),i=Y(t,n,o);L(e,s,i);}function ge(e,t,r,n,o={}){let s=e,i={};X(i,t,t.sourceOrigin,t.targetOrigin,n);for(let[u,a]of Object.entries(t.extraReplacements))X(i,t,u,a,n);for(let[u,a]of Object.entries(r))X(i,t,u,a,n);for(let[u,a]of Object.entries(o)){let l=r[u];!l||l===a||qe(i,t,a,l,n);}let c=Object.entries(i).sort((u,a)=>a[0].length-u[0].length);for(let[u,a]of c)s=Le(s,u,a);return t.urlRewriteMode!=="absolute"&&(s=De(s,t,n)),s=He(s,c.map(([,u])=>u)),_e(t,s,n)&&(s=Fe(s)),s}var S=String(process.env.PUBLISHER_WORKSPACE_BUCKET??"").trim(),w=C(process.env.PUBLISHER_WORKSPACE_PREFIX??""),_=String(process.env.PUBLISHER_ALLOWED_OPERATION??"").trim(),H=String(process.env.PUBLISHER_EXPORTER_VERSION??"unknown").trim(),Z=String(process.env.PUBLISHER_PROXY_URL??"").trim(),Qe=et(process.env.PUBLISHER_ALLOWED_TARGETS??"[]"),we=new S3Client({}),me=new Map,f=null;function et(e){try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(r=>{if(!r||typeof r!="object")return !1;let n=r;return ["id","bucketName","prefix","region"].every(o=>typeof n[o]=="string")}).map(r=>({...r,prefix:C(r.prefix)})):[]}catch{return []}}function Q(){if(!S)throw new Error("PUBLISHER_WORKSPACE_BUCKET is required.")}async function tt(){if(f){let t=await f.catch(()=>null);if(t?.isConnected())return t}let e=chromium.launch({headless:true,args:["--no-sandbox","--disable-setuid-sandbox","--disable-dev-shm-usage","--no-zygote","--single-process","--disable-gpu"],...Z?{proxy:{server:Z}}:{}});f=e;try{let t=await e;return t.once("disconnected",()=>{f===e&&(f=null);}),t}catch(t){throw f===e&&(f=null),t}}async function rt(){let e=f;if(f=null,!e)return;let t=await e.catch(()=>null);t&&await t.close().catch(()=>{});}async function nt(e){let t;for(let r=1;r<=2;r+=1){let n=await tt(),o=null;try{o=await n.newContext({viewport:e.options.viewport,ignoreHTTPSErrors:e.options.ignoreHttpsErrors,javaScriptEnabled:e.options.javaScriptEnabled,serviceWorkers:"block",userAgent:"WPSuiteStaticPublisher/remote-worker-v1"}),e.options.javaScriptEnabled&&await ot(o);let s=await ie(o);return {context:o,page:await o.newPage(),networkPolicyViolation:s.getViolation}}catch(s){t=s,await o?.close().catch(()=>{}),f=null,await n.close().catch(()=>{});}}throw t instanceof Error?t:new Error(`Unable to create a Chromium page: ${String(t)}`)}async function ot(e){await e.addInitScript(()=>{Object.defineProperty(window,"__WPSUITE_STATIC_EXPORT__",{value:true,writable:false,configurable:true});let t=r=>{if(r==null||String(r)==="")return true;try{let n=new URL(String(r),window.location.href),o=new URL(window.location.href);return n.hash="",o.hash="",n.href===o.href}catch{return false}};try{let r=window.location.assign.bind(window.location),n=window.location.replace.bind(window.location);Object.defineProperty(window.location,"assign",{configurable:!0,value:o=>{if(!t(o))return r(o)}}),Object.defineProperty(window.location,"replace",{configurable:!0,value:o=>{if(!t(o))return n(o)}}),Object.defineProperty(window.location,"reload",{configurable:!0,value:()=>{}});}catch{}});}async function st(e,t){await e.evaluate(async r=>{await new Promise(n=>{let o=0,s=700,i=Date.now(),c=()=>{window.clearInterval(u),window.scrollTo(0,0),n();},u=window.setInterval(()=>{if(Date.now()-i>=r){c();return}window.scrollBy(0,s),o+=s;let a=Math.max(document.body?.scrollHeight||0,document.documentElement?.scrollHeight||0);o>=a+window.innerHeight&&c();},120);});},t);}async function ye(e){Q();let t=k(e,w),r=await we.send(new GetObjectCommand({Bucket:S,Key:t}));if(!r.Body)throw new Error(`S3 object has no body: ${t}`);return await r.Body.transformToString("utf8")}async function N(e){return JSON.parse(await ye(e))}async function ee(e,t,r){Q();let n=k(e,w),o=typeof t=="string"?Buffer.from(t,"utf8"):t,s=await we.send(new PutObjectCommand({Bucket:S,Key:n,Body:o,ContentType:r,Metadata:{sha256:M(o)}}));return {bucket:S,key:n,sha256:M(o),bytes:o.byteLength,contentType:r,...s.ETag?{etag:s.ETag}:{},...s.VersionId?{versionId:s.VersionId}:{}}}async function it(e,t){return await ee(e,`${JSON.stringify(t,null,2)}
5
+ `,"application/json; charset=utf-8")}async function at(e){try{let t=await N(e);return t.response??t}catch(t){if(t.name==="NoSuchKey"||t.$metadata?.httpStatusCode===404)return null;throw t}}function F(e){return [...new Set(e)].filter(t=>{try{let r=new URL(t);return (r.protocol==="http:"||r.protocol==="https:")&&!r.username&&!r.password}catch{return false}}).sort()}async function ct(e,t,r){let n=null,o=null,s,i=new Set;try{let c=await nt(e);n=c.context,o=c.page,s=c.networkPolicyViolation,o.on("request",g=>i.add(g.url()));let u=await o.goto(t,{waitUntil:"domcontentloaded",timeout:e.options.navigationTimeoutMs});e.options.readiness.waitForSelector&&await o.waitForSelector(e.options.readiness.waitForSelector,{timeout:e.options.readiness.timeoutMs}).catch(()=>{}),e.options.javaScriptEnabled&&e.options.readiness.waitForFunction&&await o.waitForFunction(e.options.readiness.waitForFunction,void 0,{timeout:e.options.readiness.timeoutMs}).catch(()=>{}),e.options.javaScriptEnabled?await st(o,e.options.autoScrollTimeoutMs):await o.waitForLoadState("load",{timeout:e.options.readiness.timeoutMs}).catch(()=>{}),e.options.readiness.fallbackWaitMs>0&&await o.waitForTimeout(e.options.readiness.fallbackWaitMs);let a=s?.();if(a)throw a;let l=await o.evaluate(()=>{let g=(y,Re)=>{let D=[];for(let be of document.querySelectorAll(y))for(let te of Re){let q=be.getAttribute(te);if(q)if(te.includes("srcset"))for(let Ee of q.split(",")){let re=Ee.trim().split(/\s+/,1)[0];re&&D.push(re);}else D.push(q);}return D},I=y=>{try{return new URL(y,document.baseURI).href}catch{return null}};return {links:g("a[href]",["href"]).map(I).filter(y=>y!==null),assets:g("img,script,link,source,video,audio,iframe,[data-src],[data-href],[data-resource-url]",["src","href","srcset","data-src","data-srcset","data-href","data-resource-url","poster"]).map(I).filter(y=>y!==null)}}),p=await o.content(),d=s();if(d)throw d;let P=new URL(t),$=F(l.links).filter(g=>{let I=new URL(g);return I.hash="",I.origin===P.origin}),B=E(z(w,e.jobId,e.taskId),"pages",`${String(r).padStart(4,"0")}-${M(t).slice(0,24)}.html`),W=await ee(B,p,"text/html; charset=utf-8"),h=(await n.cookies()).map(g=>({name:g.name,domain:g.domain,path:g.path,expires:g.expires,httpOnly:g.httpOnly,secure:g.secure,sameSite:g.sameSite,..."partitionKey"in g?{partitioned:!0}:{}})),j=u?.status()??null;return {requestedUrl:t,finalUrl:o?.url()||null,outcome:j!==null&&j>=400?"http-error":"rendered",httpStatus:j,contentType:u?.headers()["content-type"]??null,html:W,discoveredPages:$,discoveredAssets:F(l.assets),networkUrls:F([...i]),cookies:h}}catch(c){return {requestedUrl:t,finalUrl:o?.url()||null,outcome:"failed",httpStatus:null,contentType:null,html:null,discoveredPages:[],discoveredAssets:[],networkUrls:F([...i]),cookies:[],error:c instanceof Error?c.message:String(c)}}finally{await n?.close().catch(()=>{});}}async function lt(e,t){if(!Array.isArray(e.urls)||e.urls.length<1||e.urls.length>20)throw new Error("Render tasks require between 1 and 20 URLs.");let r=new URL(e.sourceOrigin).origin,n=[];for(let s=0;s<e.urls.length;s+=1){let i=e.urls[s];if(!i)continue;let c=t.getRemainingTimeInMillis?.();if(c!==void 0&&c<6e4){n.push({requestedUrl:i,finalUrl:null,outcome:"deferred",httpStatus:null,contentType:null,html:null,discoveredPages:[],discoveredAssets:[],networkUrls:[],cookies:[],error:"Worker stopped accepting URLs because less than 60 seconds remained."});continue}let u=new URL(i);if(u.origin!==r)throw new Error(`Primary render URL is outside sourceOrigin: ${i}`);n.push(await ct(e,u.toString(),s));}let o=n.filter(s=>s.outcome==="failed"||s.outcome==="deferred").length;return {response:{schemaVersion:b,operation:"render",jobId:e.jobId,taskId:e.taskId,status:o>0?"partial":"succeeded",completedItems:n.length-o,failedItems:o,exporterVersion:H},detail:{pages:n}}}async function ut(e){if(!Array.isArray(e.items)||e.items.length<1||e.items.length>500)throw new Error("Rewrite tasks require between 1 and 500 objects.");let r={...await N(e.configKey),outputDir:"/workspace"},n=await N(e.assetMapKey),o=e.previousAssetMapKey?await N(e.previousAssetMapKey):{},s=[];for(let i of e.items){let c=k(i.inputKey,w),u=k(i.outputKey,w),a=await ye(c),l=`/workspace/${i.relativePath.replace(/^\/+/,"")}`,p=ge(a,r,n,l,o),d=await ee(u,p,"text/plain; charset=utf-8");s.push({inputKey:c,outputKey:u,changed:p!==a,output:d});}return {response:{schemaVersion:b,operation:"rewrite",jobId:e.jobId,taskId:e.taskId,status:"succeeded",completedItems:s.length,failedItems:0,exporterVersion:H},detail:{objects:s}}}function pt(e,t){return `${e}/${t.split("/").map(r=>encodeURIComponent(r)).join("/")}`}async function dt(e){if(!Array.isArray(e.items)||e.items.length>1e3)throw new Error("Deploy copy tasks accept at most 1000 objects.");let t=Qe.find(a=>a.id===e.targetId);if(!t)throw new Error(`Unknown deployment target: ${e.targetId}`);let r=me.get(t.region);r||(r=new S3Client({region:t.region}),me.set(t.region,r));let n=[];for(let a of e.items){let l=k(a.sourceKey,w),p=E(t.prefix,a.targetRelativeKey);try{let d=await r.send(new HeadObjectCommand({Bucket:t.bucketName,Key:p})).catch(h=>{if((h&&typeof h=="object"&&"$metadata"in h?Number(h.$metadata?.httpStatusCode):0)===404||h?.name==="NotFound")return null;throw h}),P=String(d?.CacheControl??"").replace(/\s+/g,"")===a.cacheControl.replace(/\s+/g,""),$=d?.Metadata?.["wpsuite-sha256"]===a.sha256,B=!!a.normalizedSha256&&d?.Metadata?.["wpsuite-normalized-sha256"]===a.normalizedSha256;if(P&&($||B)){n.push({sourceKey:l,targetKey:p,status:"skipped"});continue}let W=await r.send(new CopyObjectCommand({Bucket:t.bucketName,Key:p,CopySource:pt(S,l),MetadataDirective:"REPLACE",ContentType:a.contentType,CacheControl:a.cacheControl,Metadata:{"wpsuite-sha256":a.sha256,...a.normalizedSha256?{"wpsuite-normalized-sha256":a.normalizedSha256}:{}}}));n.push({sourceKey:l,targetKey:p,status:"copied",...W.CopyObjectResult?.ETag?{etag:W.CopyObjectResult.ETag}:{}});}catch(d){n.push({sourceKey:l,targetKey:p,status:"failed",error:d instanceof Error?d.message:String(d)});}}let s=(e.deleteRelativeKeys??[]).map(a=>E(t.prefix,a)),i=[],c=[];if(s.length>0){let a=await r.send(new DeleteObjectsCommand({Bucket:t.bucketName,Delete:{Objects:s.map(l=>({Key:l})),Quiet:false}}));i.push(...(a.Deleted??[]).flatMap(l=>l.Key?[l.Key]:[])),c.push(...(a.Errors??[]).map(l=>({key:l.Key??"",error:l.Message??l.Code??"S3 deletion failed"})));}let u=n.filter(a=>a.status==="failed").length+c.length;return {response:{schemaVersion:b,operation:"deploy-copy",jobId:e.jobId,taskId:e.taskId,status:u>0?"partial":"succeeded",completedItems:n.filter(a=>a.status!=="failed").length+i.length,failedItems:u,exporterVersion:H},detail:{objects:n,deletedKeys:i,deleteErrors:c,targetId:t.id}}}async function fe(e,t){if(e.operation==="health")return {response:{schemaVersion:b,operation:"health",jobId:e.jobId,taskId:e.taskId,status:"succeeded",completedItems:1,failedItems:0,exporterVersion:H},detail:{allowedOperation:_,workspaceBucket:S,workspacePrefix:w,proxyConfigured:!!Z}};if(_&&_!==e.operation)throw new Error(`Function is configured for ${_}, not ${e.operation}.`);return e.operation==="render"?await lt(e,t):e.operation==="rewrite"?await ut(e):await dt(e)}async function Ht(e,t={}){let r=se(e);try{if(r.operation==="health")return (await fe(r,t)).response;Q();let n=E(z(w,r.jobId,r.taskId),"result.json"),o=await at(n);if(o)return o;let s=await fe(r,t),i={...s.response,resultKey:n};return await it(n,{schemaVersion:b,response:i,detail:s.detail,completedAt:new Date().toISOString(),requestId:t.awsRequestId??null}),i}finally{r.operation==="render"&&await rt();}}export{Ht as handler};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smart-cloud/publisher-exporter",
3
- "version": "1.1.63",
3
+ "version": "1.1.64",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Headless Playwright static publisher for WordPress/Elementor sites with sitemap-only page discovery, strict asset capture, escaped URL rewrite, structured logs, and targeted retry modes.",