@smart-cloud/publisher-exporter 1.0.0
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.
- package/README.md +82 -0
- package/dist/cli.js +18 -0
- package/dist/crawl.js +6 -0
- package/dist/deploy.js +7 -0
- package/dist/invalidate.js +3 -0
- package/dist/queue-runner.js +3 -0
- package/package.json +65 -0
- package/publisher.config.example.json +51 -0
- package/scripts/queue-runner-cron.sh +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# @smart-cloud/publisher-exporter
|
|
2
|
+
|
|
3
|
+
`@smart-cloud/publisher-exporter` is the standalone Node.js CLI package behind WP Suite Static Publisher crawl, deploy, invalidate, and queue-runner workflows.
|
|
4
|
+
|
|
5
|
+
It is designed to run outside WordPress. The WordPress plugin only manages runtime config, queue state, and logs.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
Global install:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @smart-cloud/publisher-exporter
|
|
13
|
+
publisher-exporter install-browsers
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Without a global install:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx @smart-cloud/publisher-exporter install-browsers
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
If multiple users may run jobs on the same host, prefer a shared Playwright browsers path:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
export PLAYWRIGHT_BROWSERS_PATH=/var/lib/playwright-browsers
|
|
26
|
+
publisher-exporter install-browsers
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Commands
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
PUBLISHER_CONFIG=./publisher.config.json publisher-exporter crawl
|
|
33
|
+
PUBLISHER_CONFIG=./publisher.config.json publisher-exporter deploy
|
|
34
|
+
PUBLISHER_CONFIG=./publisher.config.json publisher-exporter invalidate
|
|
35
|
+
publisher-exporter publish
|
|
36
|
+
publisher-exporter queue-runner --runtime-dir /srv/site/runtime --max-jobs 1
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Equivalent `npx` usage:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
PUBLISHER_CONFIG=./publisher.config.json npx @smart-cloud/publisher-exporter crawl
|
|
43
|
+
PUBLISHER_CONFIG=./publisher.config.json npx @smart-cloud/publisher-exporter deploy
|
|
44
|
+
PUBLISHER_CONFIG=./publisher.config.json npx @smart-cloud/publisher-exporter invalidate
|
|
45
|
+
npx @smart-cloud/publisher-exporter queue-runner --runtime-dir /srv/site/runtime --max-jobs 1
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Queue Runner
|
|
49
|
+
|
|
50
|
+
The queue runner reads the runtime JSON files generated by the WordPress plugin.
|
|
51
|
+
|
|
52
|
+
Example:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
publisher-exporter queue-runner \
|
|
56
|
+
--runtime-dir /var/www/site/wp-content/uploads/smartcloud-static-publisher/runtime \
|
|
57
|
+
--exporter-dir /usr/local/lib/node_modules/@smart-cloud/publisher-exporter \
|
|
58
|
+
--max-jobs 1
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The package also ships a generic cron helper under `scripts/queue-runner-cron.sh`.
|
|
62
|
+
|
|
63
|
+
After each finished, failed, or stopped job, queue-runner copies the exporter-generated working logs into `"<logDir>/archive/<timestamp-command-jobId-status>/"` and includes a `job.json` summary plus the latest `current-progress.json` snapshot when available. The live root log files remain the current working set and may be overwritten by the next job.
|
|
64
|
+
|
|
65
|
+
`retry-timeouts` uses the newest archived full `crawl` or `publish` job log snapshot as its timeout source. This avoids accidentally retrying based on a later `deploy`, `url`, or other non-crawl job that happened to overwrite the live root log set.
|
|
66
|
+
|
|
67
|
+
Archives are kept as plain directories for direct inspection; they are not compressed or pruned automatically. Clean up old `archive/` subfolders periodically according to your retention policy.
|
|
68
|
+
|
|
69
|
+
## WordPress Integration
|
|
70
|
+
|
|
71
|
+
The companion WordPress plugin can optionally store an `External exporter dir` setting. Point it at this package root when you want PHP-side diagnostics to verify the local CLI install.
|
|
72
|
+
|
|
73
|
+
Typical values:
|
|
74
|
+
|
|
75
|
+
- `/usr/local/lib/node_modules/@smart-cloud/publisher-exporter`
|
|
76
|
+
- `/opt/smartcloud/publisher-exporter/node_modules/@smart-cloud/publisher-exporter`
|
|
77
|
+
|
|
78
|
+
## Notes
|
|
79
|
+
|
|
80
|
+
- Playwright browser binaries are separate from the npm package and may need reinstalling after Playwright version upgrades.
|
|
81
|
+
- For internal/self-signed TLS origins, set `ignoreHttpsErrors` in `publisher.config.json` or through the WordPress admin UI.
|
|
82
|
+
- The package expects Node.js 20 or newer.
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {spawn}from'child_process';import {existsSync}from'fs';import o from'path';import {fileURLToPath}from'url';var u=o.dirname(fileURLToPath(import.meta.url)),i=o.resolve(u,"..");function w(){console.log(`publisher-exporter <command> [args]
|
|
3
|
+
|
|
4
|
+
Commands:
|
|
5
|
+
crawl Run the crawl phase
|
|
6
|
+
deploy Deploy the prepared output
|
|
7
|
+
invalidate Request CloudFront invalidation
|
|
8
|
+
publish Run crawl, deploy, then invalidate
|
|
9
|
+
queue-runner Process queued jobs from runtime JSON
|
|
10
|
+
install-browsers Install Playwright Chromium into the current/shared browsers path
|
|
11
|
+
help Show this help
|
|
12
|
+
|
|
13
|
+
Examples:
|
|
14
|
+
publisher-exporter crawl --crawl-mode incremental
|
|
15
|
+
publisher-exporter deploy --profile prod
|
|
16
|
+
publisher-exporter queue-runner --runtime-dir /srv/site/runtime --max-jobs 1
|
|
17
|
+
publisher-exporter install-browsers
|
|
18
|
+
`);}function p(r){let e=o.join(u,`${r}.js`);if(!existsSync(e))throw new Error(`Missing CLI entrypoint: ${e}`);return e}function f(){let r=[o.join(i,"node_modules","playwright","cli.js"),o.join(i,"node_modules","playwright-core","cli.js")];for(let e of r)if(existsSync(e))return e;throw new Error("Playwright CLI was not found under this package. Run `npm install` first.")}function t(r,e){return new Promise((n,s)=>{let a=spawn(process.execPath,[r,...e],{cwd:i,env:process.env,stdio:"inherit"});a.on("error",s),a.on("close",(d,l)=>{if(l){s(new Error(`Command terminated by signal ${l}.`));return}n(d??1);});})}async function g(r){for(let e of ["crawl","deploy","invalidate"]){let n=await t(p(e),r);if(n!==0)return n}return 0}async function y(){let[r="help",...e]=process.argv.slice(2);if(["help","--help","-h"].includes(r)){w();return}if(r==="publish"){process.exitCode=await g(e);return}if(r==="install-browsers"){let n=e.length>0?e:["install","chromium"];process.exitCode=await t(f(),n);return}if(r==="crawl"||r==="deploy"||r==="invalidate"||r==="queue-runner"){process.exitCode=await t(p(r),e);return}throw new Error(`Unknown command: ${r}`)}y().catch(r=>{console.error(r instanceof Error?r.message:String(r)),process.exit(1);});
|
package/dist/crawl.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import {chromium}from'playwright';import P from'fs/promises';import f from'path';import We from'crypto';import Je from'fast-glob';function ue(e){let t=e.trim();return t==="."?".":t.replace(/\/$/,"")}function B(e){return !e||typeof e!="object"?{}:Object.fromEntries(Object.entries(e).map(([t,r])=>[t.trim(),String(r??"")]).filter(([t])=>t.length>0))}function Ne(e){if(!e||typeof e!="object")return {};let t={};for(let[r,s]of Object.entries(e)){let n=r.trim();if(!n||!s||typeof s!="object")continue;let i=s,a={};if(typeof i.targetOrigin=="string"){let l=ue(i.targetOrigin);l&&(a.targetOrigin=l);}let o=B(i.extraReplacements);if(Object.keys(o).length>0&&(a.extraReplacements=o),i.s3&&typeof i.s3=="object"){let l={},u=i.s3;typeof u.bucket=="string"&&(l.bucket=u.bucket.trim()),typeof u.prefix=="string"&&(l.prefix=u.prefix.trim()),typeof u.region=="string"&&(l.region=u.region.trim()),typeof u.htmlCacheControl=="string"&&(l.htmlCacheControl=u.htmlCacheControl.trim()),typeof u.assetCacheControl=="string"&&(l.assetCacheControl=u.assetCacheControl.trim()),Object.keys(l).length>0&&(a.s3=l);}if(i.cloudFront&&typeof i.cloudFront=="object"){let l={},u=i.cloudFront;typeof u.distributionId=="string"&&(l.distributionId=u.distributionId.trim()),Array.isArray(u.invalidationPaths)&&(l.invalidationPaths=u.invalidationPaths.map(c=>String(c??"").trim()).filter(c=>c.length>0)),Object.keys(l).length>0&&(a.cloudFront=l);}t[n]=a;}return t}function z(e,t){let s=String(e||"").replace(/\\/g,"/").trim().replace(/^\/+|\/+$/g,"");if(!s)return t;let n=s.split("/").map(i=>i.trim()).filter(i=>i.length>0&&i!=="."&&i!=="..");return n.length>0?n.join("/"):t}function _e(){let e=process.env.STATIC_PUBLISHER_RUNTIME_DIR||process.env.WPSUITE_STATIC_PUBLISHER_RUNTIME_DIR||"";return e.trim()?f.resolve(e):""}function oe(e,t,r){let s=String(t||"").trim();return s&&f.isAbsolute(s)?f.resolve(s):f.resolve(e,z(s,r))}async function G(){let e=process.env.PUBLISHER_CONFIG||"publisher.config.json",t=await P.readFile(e,"utf8"),r=JSON.parse(t);r.sourceOrigin=r.sourceOrigin.replace(/\/$/,""),r.targetOrigin=ue(r.targetOrigin),r.ignoreHttpsErrors??=false,r.outputDir=String(r.outputDir||"export").trim()||"export",r.urlRewriteMode||=r.targetOrigin==="."?"relative":"absolute",r.noJavaScriptRenderPathPrefixes||=[],r.seedPaths||=[],r.sitemapPaths||=["/sitemap_index.xml","/sitemap.xml"],r.allowedAssetHosts||=[],r.assetPathPrefixes||=["/wp-content/","/wp-includes/","/static/","/assets/","/build/","/_next/","/docs/","/sitemap","/robots.txt","/llms.txt"],r.blockedPathPrefixes||=[],r.blockedSearchFragments||=[],r.concurrency||=1,r.maxPages||=0,r.extraReplacements=B(r.extraReplacements),r.postCrawlCopyMap=B(r.postCrawlCopyMap),r.logDir=String(r.logDir||"logs").trim()||"logs",r.verbose??=false,r.logLevel||=r.verbose?"debug":"info",r.s3SyncMode||="sdk-upload-delete",r.readiness||={waitForSelector:null,waitForFunction:null,timeoutMs:1500,fallbackWaitMs:1500},r.readiness.timeoutMs??=1500,r.readiness.fallbackWaitMs??=1500,r.viewport||={width:1440,height:1200},r.navigationTimeoutMs||=3e4,r.scheduler||={enabled:false,timezone:"UTC",rules:[]},r.scheduler.enabled??=false,r.scheduler.timezone||="UTC",r.scheduler.rules||=[],r.deploymentProfiles=Ne(r.deploymentProfiles),r.assetDownloadConcurrency=Number(r.assetDownloadConcurrency)>0?Number(r.assetDownloadConcurrency):r.concurrency,r.rewriteConcurrency=Number(r.rewriteConcurrency)>0?Number(r.rewriteConcurrency):r.assetDownloadConcurrency,r.defaultDeploymentProfile=String(r.defaultDeploymentProfile??"").trim(),r.defaultDeploymentProfile&&!r.deploymentProfiles[r.defaultDeploymentProfile]&&(r.defaultDeploymentProfile="");let s=_e(),n=s?f.resolve(s,".."):"";return n?(r.outputDir=oe(n,r.outputDir,"export"),r.logDir=oe(n,r.logDir,"logs")):(f.isAbsolute(r.outputDir)||(r.outputDir=z(r.outputDir,"export")),f.isAbsolute(r.logDir)||(r.logDir=z(r.logDir,"logs"))),r}function ce(e,t=10){return We.createHash("sha1").update(e).digest("hex").slice(0,t)}async function V(e){await P.mkdir(f.dirname(e),{recursive:true});}function F(e){return [".html",".htm",".css",".js",".mjs",".json",".xml",".xsl",".txt",".svg",".map",".enc",".jws"].includes(f.extname(e).toLowerCase())}function L(e){return e.replace(/"/g,'"').replace(/"/g,'"').replace(/'/g,"'").replace(/'/g,"'").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function T(e){return e.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">")}function O(e){return e.replace(/\//g,"\\/")}function le(e){return e.replace(/\//g,"\\\\/")}function de(e,t,r){if(!t)return;e[t]=r;let s=O(t),n=O(r);e[s]=n;let i=le(t),a=le(r);e[i]=a;let o=T(t),l=T(r);e[o]=l;let u=T(s),c=T(n);e[u]=c;let d=T(i),g=T(a);e[d]=g;}function K(e,t){let r=t.startsWith("/")?t:`/${t}`,s=(e||"").replace(/\/$/,"");return !s||s==="."||s==="/"?r:`${s}${r}`}function pe(e,t){try{return new URL(e,t==="."?"https://relative.invalid":t).pathname}catch{return e.startsWith("/")?e:`/${e.replace(/^\.\//,"")}`}}function ge(e,t,r){if(!t)return r;let s=f.dirname(f.resolve(t)),n=f.resolve(e,r.replace(/^\/+/,"")),i=f.relative(s,n).replace(/\\/g,"/");return i?(i.startsWith(".")||(i=`./${i}`),i):"."}var Be=["wp-content/","wp-includes/","wp-admin/","wp-json/","_next/"],ze=new Set([".html",".htm"]),he="WPSuite.io Static Publisher",Ge=he.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");function Ve(e,t,r){if(e.wpsuite?.subscriber===true||!r)return false;let s=f.extname(r).toLowerCase();return ze.has(s)?/<head\b|<html\b|<!doctype html/i.test(t):false}function Ke(e){if(new RegExp(`<meta\\b(?=[^>]*\\bname=(["'])generator\\1)(?=[^>]*\\bcontent=(["'])${Ge}\\2)[^>]*\\/?>`,"i").test(e))return e;let t=`<meta name="generator" content="${he}" />`;if(e.match(/(\r?\n)([ \t]*)<\/head>/i))return e.replace(/(\r?\n)([ \t]*)<\/head>/i,`$1$2${t}$1$2</head>`);let s=e.includes(`\r
|
|
2
|
+
`)?`\r
|
|
3
|
+
`:`
|
|
4
|
+
`;return e.replace(/<head\b[^>]*>/i,n=>`${n}${s} ${t}`)}function Xe(e){let t=L(e).trim();return t.startsWith("{")||t.startsWith("[")||t.includes("\\/")||/"@context"|"@type"/.test(t)}function Ye(e,t){let r=[...new Set(t)].filter(i=>i.includes("/")&&!/\\+\//.test(i)).map(i=>[i,O(i)]).filter(([i,a])=>i!==a).sort((i,a)=>a[0].length-i[0].length);if(!r.length)return e;let s=i=>{let a=i;for(let[o,l]of r)a=a.split(o).join(l);return a},n=e.replace(/(<script\b[^>]*\btype=["']application\/(?:ld\+)?json["'][^>]*>)([\s\S]*?)(<\/script>)/gi,(i,a,o,l)=>`${a}${s(o)}${l}`);return n=n.replace(/(<meta\b[^>]*\bcontent=(['"]))([\s\S]*?)(\2[^>]*>)/gi,(i,a,o,l,u)=>Xe(l)?`${a}${s(l)}${u}`:i),n}function Ze(e,t){if(!t)return null;let r=f.dirname(f.resolve(t)),s=f.resolve(e.outputDir),n=f.relative(r,s).replace(/\\/g,"/");return n?(n.startsWith(".")||(n=`./${n}`),n.endsWith("/")?n:`${n}/`):"./"}function et(e,t,r){let s=Ze(t,r);if(!s)return e;let n=e;for(let i of Be){let a=i.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),o=new RegExp(`(?:\\.{1,}/)+${a}`,"g"),l=new RegExp(`(?<!\\.)/${a}`,"g");n=n.replace(o,`${s}${i}`).replace(l,`${s}${i}`);let u=i.replace(/\//g,"\\/"),c=s.replace(/\//g,"\\/"),d=u.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),g=new RegExp(`(?:\\.{1,}\\\\/)+${d}`,"g"),m=new RegExp(`(?:\\.{1,}(?:\\\\/|\\\\))+${d}`,"g"),h=new RegExp(`(?<![\\.\\\\])\\/${d}`,"g");n=n.replace(g,`${c}${u}`).replace(m,`${c}${u}`).replace(h,`${c}${u}`);}return n}function tt(e,t,r){if(e.urlRewriteMode==="absolute")return t;let s=(()=>{try{return new URL(t,e.targetOrigin==="."?"https://relative.invalid":e.targetOrigin)}catch{return null}})(),n=s?s.pathname:pe(t,e.targetOrigin),i=s?`${s.search}${s.hash}`:"";return e.urlRewriteMode==="root-relative"?`${n}${i}`:`${ge(e.outputDir,r,n)}${i}`}function X(e,t,r,s,n){de(e,r,tt(t,s,n));}function rt(e,t,r,s){let n=e,i={};X(i,t,t.sourceOrigin,t.targetOrigin,s);for(let[o,l]of Object.entries(t.extraReplacements))X(i,t,o,l,s);for(let[o,l]of Object.entries(r))X(i,t,o,l,s);let a=Object.entries(i).sort((o,l)=>l[0].length-o[0].length);for(let[o,l]of a)n=n.split(o).join(l);return t.urlRewriteMode!=="absolute"&&(n=et(n,t,s)),n=Ye(n,a.map(([,o])=>o)),Ve(t,n,s)&&(n=Ke(n)),n}async function fe(e,t,r={}){let s=r.files?[...new Set(r.files)].map(c=>f.resolve(c)).filter(c=>c.startsWith(f.resolve(e.outputDir))).map(c=>f.relative(e.outputDir,c).replace(/\\/g,"/")).filter(c=>c.length>0):await Je(["**/*"],{cwd:e.outputDir,onlyFiles:true,dot:true}),n=0;async function i(c){let d=f.join(e.outputDir,c);if(F(d)&&c!=="asset-map.json"){let g=f.extname(d).toLowerCase(),m;try{m=await P.readFile(d,"utf8");}catch(w){if(w.code!=="ENOENT")throw w;return {changed:false}}let p=rt(m,e,t,g===".js"||g===".mjs"?void 0:d);if(p!==m)return await P.writeFile(d,p,"utf8"),{changed:true}}return {changed:false}}let a=Math.max(1,Number(e.rewriteConcurrency||e.assetDownloadConcurrency||1)),o=0,l=0,u=Array.from({length:Math.min(a,Math.max(s.length,1))},async()=>{for(;;){let c=o;if(c>=s.length)return;o+=1;let d=s[c];(await i(d)).changed&&(n+=1),l+=1,await r.onProgress?.({index:l,totalFiles:s.length,changedTextFiles:n,file:d});}});return await Promise.all(u),n}var E={error:0,warn:1,info:2,debug:3},we=process.env.STATIC_PUBLISHER_RUNTIME_DIR||process.env.WPSUITE_STATIC_PUBLISHER_RUNTIME_DIR||"",U=we?f.join(we,"current-progress.json"):"",Pe=Promise.resolve();function ye(e){return e.includes("crawl")?"crawl":e.includes("deploy")?"deploy":e.includes("invalidate")?"invalidate":e}function st(e,t){let r={...e},s=new Set(["pagesQueued","pagesRendered","assetsQueued","sitemapsQueued","pagesDiscovered","assetsDiscovered","sitemapsDiscovered","pagesSaved","assetsSaved","donePages","doneAssets","doneSitemaps","changedTextFiles","uploaded","failed","index","totalFiles","pageQueue","assetQueue","sitemapQueue"]);for(let[n,i]of Object.entries(t)){let a=r[n];if(typeof a=="number"&&typeof i=="number"&&s.has(n)){r[n]=Math.max(a,i);continue}r[n]=i;}return r}function nt(e,t,r){U&&(Pe=Pe.then(async()=>{let s=new Date,n=s.toISOString();await P.mkdir(f.dirname(U),{recursive:true});let i=await P.readFile(U,"utf8").then(p=>JSON.parse(p)).catch(()=>null),a=i&&typeof i.details=="object"&&i.details?i.details:{},o=ye(e),l=i?.currentStep||ye(i?.source||""),u=i?.startedAt||n,c={...i?.stepDurationsSec??{}},d=i?.stepStartedAt||n;if(l&&l!==o&&i?.stepStartedAt){let p=Math.max(0,Math.round((s.getTime()-new Date(i.stepStartedAt).getTime())/1e3));c[l]=(c[l]??0)+p,d=n;}let g=Math.max(0,Math.round((s.getTime()-new Date(d).getTime())/1e3)),m=Math.max(0,Math.round((s.getTime()-new Date(u).getTime())/1e3)),h={checkedAt:n,source:e,message:t,details:st(a,r??{}),startedAt:u,currentStep:o,stepStartedAt:d,stepElapsedSec:g,totalElapsedSec:m,stepDurationsSec:c};await P.writeFile(U,JSON.stringify(h,null,2),"utf8");}).catch(()=>{}));}async function it(e,t){await P.mkdir(f.dirname(e),{recursive:true}),await P.appendFile(e,`${JSON.stringify(t)}
|
|
5
|
+
`,"utf8");}function at(e,t){let r=typeof t?.phase=="string"?t.phase.trim():"";return r||(e==="page"?"render-pages":e==="asset"?"download-assets":e==="sitemap"?"discovery":e==="timing"&&typeof t?.name=="string"&&t.name.trim()||"crawl")}function ot(e,t,r){return {checkedAt:new Date().toISOString(),currentStep:at(e,r),level:e,message:t,details:r??{}}}var $=class{constructor(t,r="info"){this.logDir=t;this.level=typeof r=="boolean"?r?"debug":"info":r,this.ensureLogFileReady();}logDir;startedAt=Date.now();marks=new Map;initPromise=null;writeQueue=Promise.resolve();writeError=null;level;get logPath(){return f.join(this.logDir,"crawl.log.jsonl")}get currentEventPath(){return f.join(this.logDir,"current-crawl-event.json")}get rejectedPath(){return f.join(this.logDir,"rejected.jsonl")}get ignoredPath(){return f.join(this.logDir,"ignored.jsonl")}get skippedPath(){return f.join(this.logDir,"skipped-http.jsonl")}get errorsPath(){return f.join(this.logDir,"errors.jsonl")}get timingsPath(){return f.join(this.logDir,"timings.jsonl")}ensureLogFileReady(){return this.initPromise||(this.initPromise=(async()=>{await P.mkdir(this.logDir,{recursive:true}),await P.writeFile(this.logPath,"","utf8");})()),this.initPromise}enqueueTask(t){this.writeQueue=this.writeQueue.then(t).catch(r=>{this.writeError=r instanceof Error?r:new Error(String(r));});}enqueueLine(t){this.enqueueTask(async()=>{await this.ensureLogFileReady(),await P.appendFile(this.logPath,`${t}
|
|
6
|
+
`,"utf8");});}enqueueJsonLine(t,r){this.enqueueTask(()=>it(t,r));}updateCurrentEvent(t,r,s){let n=ot(t,r,s);this.enqueueTask(async()=>{await P.mkdir(this.logDir,{recursive:true}),await P.writeFile(this.currentEventPath,JSON.stringify(n,null,2),"utf8");});}push(t,r,s){let n=JSON.stringify({time:new Date().toISOString(),level:t,message:r,...s||{}});this.enqueueLine(n),this.updateCurrentEvent(t,r,s),t==="error"?console.error(r,s||""):t==="warn"?console.warn(r,s||""):(E[this.level]>=E.debug||["summary","page","sitemap","asset","timing"].includes(t))&&console.log(r);}info(t,r){this.push("info",t,r);}progress(t,r){nt("crawl.log.jsonl",t,r),this.updateCurrentEvent("progress",t,r);}checkpoint(t,r){this.updateCurrentEvent("checkpoint",t,r);}page(t,r){this.push("page",t,r);}sitemap(t,r){this.push("sitemap",t,r);}asset(t,r){this.push("asset",t,r);}warn(t,r){this.push("warn",t,r);}error(t,r){this.enqueueJsonLine(this.errorsPath,{message:t,...r||{}}),this.push("error",t,r);}summary(t,r){this.push("summary",t,r);}mark(t){this.marks.set(t,Date.now());}endMark(t,r){let s=this.marks.get(t);if(!s)return;let n=Date.now()-s,i={name:t,ms:n,seconds:Number((n/1e3).toFixed(2)),...r||{}};this.enqueueJsonLine(this.timingsPath,i),this.push("timing",`Timing ${t}: ${i.seconds}s`,i);}reject(t,r,s,n){let i={kind:t,url:r,reason:s,source:n};this.enqueueJsonLine(this.rejectedPath,i),E[this.level]>=E.debug&&this.push("reject",`Rejected ${t} ${r}: ${s}`,i);}ignore(t,r,s,n){let i={kind:t,url:r,reason:s,source:n};this.enqueueJsonLine(this.ignoredPath,i),E[this.level]>=E.debug&&this.push("ignore",`Ignored ${t} ${r}: ${s}`,i);}skip(t,r,s,n){let i={kind:t,url:r,status:s,source:n};this.enqueueJsonLine(this.skippedPath,i),this.push("warn",`Skipped ${t} ${r}: HTTP ${s}`,i);}async flush(){let t=Date.now()-this.startedAt,r={name:"total",ms:t,seconds:Number((t/1e3).toFixed(2))};if(this.enqueueJsonLine(this.timingsPath,r),await this.writeQueue,this.writeError)throw this.writeError}};var ut=["text/css","javascript","image/","font/","application/font","application/json","application/xml","text/xml","application/rss+xml","application/atom+xml","application/xslt+xml","text/xsl","image/svg+xml","application/octet-stream"],lt=new Set([".css",".js",".mjs",".json",".map",".xml",".xsl",".rss",".atom",".txt",".enc",".jws",".png",".jpg",".jpeg",".gif",".webp",".avif",".svg",".ico",".woff",".woff2",".ttf",".otf",".eot",".pdf",".mp4",".webm"]),ct=new Set(["",".html",".htm"]),dt=new Set([".css",".xml",".xsl",".svg",".json",".html",".htm",".txt",".enc",".jws"]),pt=["/wp-content/","/wp-includes/","/wp-admin/","/static/","/assets/","/build/","/_next/"];function gt(e){let t=[],r="full",s="full",n=false;for(let i=0;i<e.length;i++){let a=e[i];if(a==="--retry-timeouts")r="retry-timeouts",n=true;else if(a==="--resume")n=true;else if(a==="--crawl-mode"){let o=e[++i];if(!o)throw new Error("--crawl-mode requires a value");s=o==="incremental"?"incremental":"full",n||=s==="incremental";}else if(a.startsWith("--crawl-mode="))s=a.slice(13)==="incremental"?"incremental":"full",n||=s==="incremental";else if(a==="--incremental")s="incremental",n=true;else if(a==="--url"){let o=e[++i];if(!o)throw new Error("--url requires a value");r="single-url",n=true,t.push(o);}else if(a.startsWith("--url="))r="single-url",n=true,t.push(a.slice(6));else if(a==="--urls"){let o=e[++i];if(!o)throw new Error("--urls requires a file path");r="single-url",n=true,t.push(`@${o}`);}}return {mode:r,crawlMode:s,urls:t,preserveOutput:n}}function te(){let e=process.env.STATIC_PUBLISHER_RUNTIME_DIR||process.env.WPSUITE_STATIC_PUBLISHER_RUNTIME_DIR||"";return e.trim()?f.resolve(e):""}function mt(){let e=te();return e?f.resolve(e,".."):""}function ht(){let e=process.env.STATIC_PUBLISHER_WP_ROOT||process.env.WPSUITE_STATIC_PUBLISHER_WP_ROOT||"";return e.trim()?f.resolve(e):""}function ft(e){let t=e.trim(),r=[{alias:"@storage-root",root:mt(),requiredEnv:"STATIC_PUBLISHER_RUNTIME_DIR or WPSUITE_STATIC_PUBLISHER_RUNTIME_DIR"},{alias:"@runtime",root:te(),requiredEnv:"STATIC_PUBLISHER_RUNTIME_DIR or WPSUITE_STATIC_PUBLISHER_RUNTIME_DIR"},{alias:"@wp-root",root:ht(),requiredEnv:"STATIC_PUBLISHER_WP_ROOT or WPSUITE_STATIC_PUBLISHER_WP_ROOT"}];for(let s of r){if(t!==s.alias&&!t.startsWith(`${s.alias}/`))continue;if(!s.root)return {resolvedPath:null,alias:s.alias,requiredEnv:s.requiredEnv};let n=t.slice(s.alias.length).replace(/^\/+/,"");return {resolvedPath:n?f.resolve(s.root,n):s.root,alias:s.alias}}return {resolvedPath:f.resolve(t)}}function je(e){let t=te();return t?f.join(t,"crawl-manifest.json"):f.join(e.outputDir,".crawl-manifest.json")}async function wt(e){try{let t=await P.readFile(je(e),"utf8"),r=JSON.parse(t);if(r&&typeof r=="object"&&r.schemaVersion===1&&r.pages&&typeof r.pages=="object")return r}catch{}return {schemaVersion:1,updatedAt:"",pages:{}}}function Se(e){return JSON.parse(JSON.stringify(e))}async function Pt(e,t){let r=je(e);await P.mkdir(f.dirname(r),{recursive:true}),await P.writeFile(r,JSON.stringify(t,null,2),"utf8");}function q(e){if(!e)return null;let t=e.trim();return t||null}async function yt(e,t,r,s){let n=[...new Set(r.map(o=>o.trim()).filter(Boolean))],i=String(e.wpsuite?.siteKey||"").trim();if(n.length===0||!i)return new Map;let a=new URL("/wp-json/smartcloud-static-publisher/v1/change-tokens",e.sourceOrigin).toString();try{let o=await t.post(a,{timeout:3e4,failOnStatusCode:!1,headers:{"content-type":"application/json","x-site-key":i},data:JSON.stringify({urls:n})});if(!o.ok())return s.warn(`Change token lookup failed with HTTP ${o.status()}`,{endpoint:a,status:o.status()}),new Map;let l=await o.json().catch(()=>null);if(!l||!Array.isArray(l.items))return new Map;let u=new Map;for(let c of l.items)!c||typeof c.url!="string"||u.set(c.url,c);return u}catch(o){return s.warn("Change token lookup failed; falling back to sitemap metadata",{endpoint:a,error:String(o)}),new Map}}async function ve(e,t,r,s,n){if(!r.enabled)return null;if(r.changeTokenCache.has(s))return r.changeTokenCache.get(s)??null;let a=(await yt(e,t,[s],n)).get(s)??null;return r.changeTokenCache.set(s,a),a}async function St(e,t,r,s,n,i){if(!s.enabled)return {action:"render",changeToken:null};let a=s.manifest.pages[n];if(!a)return {action:"render",changeToken:await ve(e,t,s,n,i)};let o=await ve(e,t,s,n,i);if(o?.supported&&o.token)return a.changeToken===o.token?{action:"reuse",changeToken:o}:{action:"render",changeToken:o};let l=q(r.sitemapLastmodByPage[n]);return l&&a.sitemapLastmod&&a.sitemapLastmod===l?{action:"reuse",changeToken:o}:{action:"render",changeToken:o}}async function vt(e,t,r,s){let n=be(r.previousManifest),i=0;for(let[d,g]of Object.entries(r.manifest.pages))if(!r.seenPages.has(d)){try{await P.unlink(g.outputPath);}catch(m){(m.code||"")!=="ENOENT"&&s.warn(`Failed to remove stale incremental page output for ${d}`,{url:d,outputPath:g.outputPath,error:String(m)});}delete r.manifest.pages[d],i++;}let a=be(r.manifest),o=new Set,l=new Set;for(let d of a){for(let m of Re(e,d))o.add(m);let g=N(e,t.assetMap,d);g&&l.add(g);}let u=0,c=0;for(let d of n){if(a.has(d))continue;let g=new Set,m=N(e,t.assetMap,d);if(m)g.add(m);else {let h=ne(e,d);g.add(h.originalFilePath),h.hashedFilePath&&g.add(h.hashedFilePath);}for(let h of g)if(!l.has(h))try{await P.unlink(h),u++;}catch(p){(p.code||"")!=="ENOENT"&&s.warn(`Failed to remove stale incremental asset output for ${d}`,{url:d,outputPath:h,error:String(p)});}for(let h of Re(e,d))o.has(h)||Object.prototype.hasOwnProperty.call(t.assetMap,h)&&(delete t.assetMap[h],c++);}(i>0||u>0||c>0)&&s.info(`Incremental cleanup removed ${i} stale pages, ${u} stale assets, ${c} stale asset mappings`,{removedPages:i,removedAssets:u,removedAssetMappings:c,phase:"incremental-cleanup"});}function be(e){let t=new Set;for(let r of Object.values(e.pages))for(let s of r.discoveredAssets||[]){let n=s.trim();n&&t.add(n);}return t}function Re(e,t){let r=new Set([t]);try{let s=new URL(t),i=[new URL(e.sourceOrigin).origin,...Object.keys(e.extraReplacements||{})];if(e.targetOrigin&&e.targetOrigin!=="."&&e.targetOrigin!=="/")try{i.push(new URL(e.targetOrigin).origin);}catch{}for(let a of i)try{r.add(new URL(a).origin+s.pathname+s.search);}catch{}r.add(s.pathname+s.search),r.add(s.pathname);}catch{}return [...r]}function bt(e,t){let r=e.targetOrigin&&e.targetOrigin!=="."&&e.targetOrigin!=="/"?e.targetOrigin:"https://relative.invalid/";try{let s=new URL(t,r);return f.join(e.outputDir,decodeURIComponent(s.pathname))}catch{return null}}function N(e,t,r){let s=t[r];return s?bt(e,s):ne(e,r).originalFilePath}function Rt(){let e=process.env.STATIC_PUBLISHER_RUNTIME_DIR||process.env.WPSUITE_STATIC_PUBLISHER_RUNTIME_DIR||"";return e.trim()?f.join(f.resolve(e),"queue-runner-heartbeat.json"):""}async function xe(e){let t=Rt();if(t)try{let r=await P.readFile(t,"utf8"),s=JSON.parse(r);if(!s||typeof s!="object")return;await P.writeFile(t,JSON.stringify({...s,checkedAt:new Date().toISOString(),status:"running",currentStep:"rewrite",message:e},null,2),"utf8");}catch{}}function xt(e,t){let r=new Set;for(let s of t.donePages)r.add(f.resolve(se(e,s)));for(let s of t.doneAssets){let n=N(e,t.assetMap,s);!n||!F(n)||r.add(f.resolve(n));}return [...r]}var _="re:",Q=new Map;function Ct(e){if(!e.startsWith(_))return null;let t=Q.get(e);if(t!==void 0)return t||null;let r=e.slice(_.length).trim();if(!r)return Q.set(e,false),null;try{let s=new RegExp(r);return Q.set(e,s),s}catch{return console.warn(`[crawl] Ignoring invalid path matcher regex: ${e}`),Q.set(e,false),null}}function re(e,t){let r=Ct(t);return r?r.test(e):t.startsWith(_)?false:e.startsWith(t)}function kt(e,t){return t.find(r=>re(e,r))}function De(e,t){let r=kt(t.pathname,e.blockedPathPrefixes);if(r)return r.startsWith(_)?`blocked path regex: ${r}`:`blocked path prefix: ${r}`;let s=e.blockedSearchFragments.find(n=>t.search.includes(n));return s?`blocked search fragment: ${s}`:null}function Lt(e){let t=new URL(e.sourceOrigin),r=new Set([t.hostname,...e.allowedAssetHosts]);if(e.targetOrigin&&e.targetOrigin!=="."&&e.targetOrigin!=="/")try{r.add(new URL(e.targetOrigin).hostname);}catch{}for(let s of Object.keys(e.extraReplacements||{}))try{r.add(new URL(s).hostname);}catch{}return r}function Mt(e,t){return Lt(e).has(t.hostname)}function Tt(e){let t=L(e.trim()).replace(/\\\//g,"/").replace(/^['"]|['"]$/g,"").trim();return t=t.replace(/[)]+$/g,"").trim(),t=t.replace(/;.*$/g,"").trim(),t}function Oe(e){let t=L(e).replace(/\\\//g,"/");return /[{}]|\bwindow\.|\blocation\.|\bincludes\(|\?\?null|\+|%7B|%7D|%22|<|>|\s/.test(t)}function H(e){return pt.some(t=>e.pathname.startsWith(t))}function v(e,t,r=e.sourceOrigin,s,n="url"){let i=Tt(t);if(!i||i.startsWith("data:")||i.startsWith("blob:")||i.startsWith("mailto:")||i.startsWith("tel:")||i.startsWith("#"))return s?.ignore(n,t,"empty or unsupported scheme",r),null;if(Oe(i))return s?.ignore(n,t,"looks like JavaScript/code fragment",r),null;try{let a=new URL(i,r);if(!["http:","https:"].includes(a.protocol))return s?.ignore(n,t,"unsupported protocol: ",r),null;if(!Mt(e,a))return s?.ignore(n,a.toString(),"host not allowed",r),null;let o=De(e,a);if(o)return s?.reject(n,a.toString(),o,r),null;let l=new URL(e.sourceOrigin);return a.protocol=l.protocol,a.host=l.host,a.hash="",a}catch{return s?.reject(n,t,"invalid URL",r),null}}function Fe(e){return lt.has(f.extname(e.pathname).toLowerCase())}function D(e){return H(e)?false:ct.has(f.extname(e.pathname).toLowerCase())}function J(e){let t=e.pathname.toLowerCase();return t.endsWith(".xml")&&(t.includes("sitemap")||t.endsWith("/sitemap.xml"))}function y(e,t){return J(t)||t.pathname==="/robots.txt"||t.pathname==="/llms.txt"||t.pathname.toLowerCase().endsWith(".xsl")?true:Fe(t)?e.assetPathPrefixes.length===0?true:e.assetPathPrefixes.some(r=>re(t.pathname,r)):false}function se(e,t){let r=new URL(t),s=decodeURIComponent(r.pathname);return s.endsWith("/")?s+="index.html":f.extname(s)||(s+="/index.html"),f.join(e.outputDir,s)}function ne(e,t){let r=new URL(t),s=decodeURIComponent(r.pathname);s.endsWith("/")&&(s+="index.html");let n=!!r.search,i=s,a=f.join(e.outputDir,i),o=K(e.targetOrigin,i),l=`${o}${r.search}`;if(!n)return {originalPathname:i,originalFilePath:a,originalPublicUrl:o,originalPublicUrlWithSearch:l,preferQueryHashed:n};let u=f.extname(s),d=`${u?s.slice(0,-u.length):s}.${ce(r.search)}${u||".bin"}`;return {originalPathname:i,originalFilePath:a,originalPublicUrl:o,originalPublicUrlWithSearch:l,preferQueryHashed:n,hashedPathname:d,hashedFilePath:f.join(e.outputDir,d),hashedPublicUrl:K(e.targetOrigin,d)}}function At(e,t,r,s,n){t[r]=s;try{let i=new URL(r),o=[new URL(e.sourceOrigin).origin,...Object.keys(e.extraReplacements||{})];if(e.targetOrigin&&e.targetOrigin!=="."&&e.targetOrigin!=="/")try{o.push(new URL(e.targetOrigin).origin);}catch{}for(let l of o)try{let u=new URL(l);t[u.origin+i.pathname+i.search]=s;}catch{}try{let l=(()=>{let u=new URL(s,e.targetOrigin==="."?"https://relative.invalid":e.targetOrigin);return `${u.pathname}${u.search}`})();t[i.pathname+i.search]=l;}catch{}try{let l=new URL(s,e.targetOrigin==="."?"https://relative.invalid":e.targetOrigin).pathname;t[i.pathname]=l;}catch{}try{let l=new URL(n,e.targetOrigin==="."?"https://relative.invalid":e.targetOrigin).pathname;t[i.pathname]=l;}catch{}}catch{}}async function Z(e){try{return await P.readFile(e)}catch(t){if(t.code==="ENOENT")return null;throw t}}async function Ue(e){try{return await P.stat(e)}catch(t){if(t.code==="ENOENT")return null;throw t}}function Et(e){let t=String(e["content-length"]||"").trim();if(!t)return null;let r=Number.parseInt(t,10);return Number.isFinite(r)&&r>=0?r:null}function Qe(e){let t=String(e["last-modified"]||"").trim();if(!t)return null;let r=Date.parse(t);return Number.isFinite(r)?r:null}function It(e,t){let r=Qe(t),s=Et(t),n=e.mtime.getTime();return r!==null&&Math.abs(n-r)>=1e3||s!==null&&e.size!==s?false:r!==null||s!==null}async function $t(e,t){if(!t)return;let r=Qe(t);if(r===null)return;let s=await Ue(e);if(!s)return;let n=new Date(r);await P.utimes(e,s.atime,n).catch(()=>{});}function Ce(e,t){let r=f.extname(e.pathname).toLowerCase(),s=t["content-type"]||"";return dt.has(r)||ut.some(n=>s.includes(n))&&!s.startsWith("image/")&&!s.includes("font")}function ke(e,t){(e.doneAssets.size%25===0||e.doneAssets.size===1||e.doneAssets.size===e.stats.assetsQueued)&&t.progress(`Asset download progress: downloaded ${e.doneAssets.size}, discovered ${e.stats.assetsQueued}.`,{doneAssets:e.doneAssets.size,assetsQueued:e.stats.assetsQueued,phase:"download-assets"});}async function ee(e,t,r,s,n,i,a){let o=ne(e,r),l=o.originalFilePath,u=o.originalPublicUrlWithSearch;if(o.preferQueryHashed&&o.hashedFilePath&&o.hashedPublicUrl){let d=await Z(o.originalFilePath);d!==null&&!d.equals(s)&&(l=o.hashedFilePath,u=o.hashedPublicUrl);}At(e,t,r,u,o.originalPublicUrl);let c=await Z(l);(c===null||!c.equals(s))&&(await V(l),await P.writeFile(l,s)),await $t(l,a),n.stats.assetsSaved++,i&&(n.stats.assetsSaved%50===0||n.stats.assetsSaved===1)&&i.progress(`Asset progress: saved ${n.stats.assetsSaved}, discovered ${n.stats.assetsQueued}, pending ${n.assetQueue.length}.`,{assetsSaved:n.stats.assetsSaved,assetsQueued:n.stats.assetsQueued,assetQueue:n.assetQueue.length,phase:"download-assets"});}function W(e,t,r,s,n){let i=n&&/^https?:\/\//i.test(n)?n:e.sourceOrigin,a=v(e,r,i,s,"page");if(!a)return;if(!D(a)||Fe(a)){if(y(e,a)){s.info(`Seeded/non-page URL queued as asset: ${a.toString()}`,{url:a.toString(),source:n}),k(e,t,a.toString(),s,n);return}s.reject("page",a.toString(),H(a)?"asset/internal path cannot be page":"not page-like",n);return}let o=a.toString();!t.donePages.has(o)&&!t.queuedPages.has(o)&&(t.queuedPages.add(o),t.pageQueue.push(o),t.stats.pagesQueued++,(t.stats.pagesQueued%25===0||t.stats.pagesQueued===1)&&s.progress(`Discovery progress: pages ${t.stats.pagesQueued}, assets ${t.stats.assetsQueued}, sitemaps ${t.stats.sitemapsQueued}.`,{pagesQueued:t.stats.pagesQueued,assetsQueued:t.stats.assetsQueued,sitemapsQueued:t.stats.sitemapsQueued,pageQueue:t.pageQueue.length,phase:"discovery"}));}function k(e,t,r,s,n){let i=n&&/^https?:\/\//i.test(n)?n:e.sourceOrigin,a=v(e,r,i,s,"asset");if(!a)return;if(!y(e,a)){s.reject("asset",a.toString(),"not a safe asset path/prefix",n);return}let o=a.toString();!t.doneAssets.has(o)&&!t.queuedAssets.has(o)&&(t.queuedAssets.add(o),t.assetQueue.push(o),t.stats.assetsQueued++,(t.stats.assetsQueued%100===0||t.stats.assetsQueued===1)&&s.progress(`Discovery progress: pages ${t.stats.pagesQueued}, assets ${t.stats.assetsQueued}, sitemaps ${t.stats.sitemapsQueued}.`,{pagesQueued:t.stats.pagesQueued,assetsQueued:t.stats.assetsQueued,sitemapsQueued:t.stats.sitemapsQueued,assetQueue:t.assetQueue.length,phase:"discovery"}));}function ie(e,t,r,s,n){let i=n&&/^https?:\/\//i.test(n)?n:e.sourceOrigin,a=v(e,r,i,s,"sitemap");if(!a)return;if(!J(a)){s.reject("sitemap",a.toString(),"not sitemap-like",n);return}let o=a.toString();!t.doneSitemaps.has(o)&&!t.queuedSitemaps.has(o)&&(t.queuedSitemaps.add(o),t.sitemapQueue.push(o),t.stats.sitemapsQueued++,(t.stats.sitemapsQueued%10===0||t.stats.sitemapsQueued===1)&&s.progress(`Discovery progress: pages ${t.stats.pagesQueued}, assets ${t.stats.assetsQueued}, sitemaps ${t.stats.sitemapsQueued}.`,{pagesQueued:t.stats.pagesQueued,assetsQueued:t.stats.assetsQueued,sitemapsQueued:t.stats.sitemapsQueued,sitemapQueue:t.sitemapQueue.length,phase:"discovery"}));}function jt(e){let t=L(e),r=[...t.matchAll(/<url>\s*([\s\S]*?)\s*<\/url>/gi)].map(s=>s[1]);return r.length>0?r.map(s=>{let n=s.match(/<loc>\s*([^<]+?)\s*<\/loc>/i);if(!n?.[1])return null;let i=s.match(/<lastmod>\s*([^<]+?)\s*<\/lastmod>/i);return {loc:n[1].trim(),lastmod:q(i?.[1]??null)}}).filter(s=>!!s):[...t.matchAll(/<loc>\s*([^<]+?)\s*<\/loc>/gi)].map(s=>({loc:s[1].trim(),lastmod:null})).filter(s=>s.loc)}function Dt(e){return e.split(",").map(t=>t.trim().split(/\s+/)[0]).filter(Boolean)}function Ot(e){let t=[],r=/url\(\s*(?:"([^"]+)"|'([^']+)'|([^)]*?))\s*\)/gi;for(let s of e.matchAll(r)){let n=(s[1]||s[2]||s[3]||"").trim();n&&t.push(n);}return t}function Y(e){return e.trim().replace(/\\/g,"/").replace(/^https?:\/\/[^/]+/i,"").replace(/^\/+/,"")}async function Ft(e,t){await P.mkdir(t,{recursive:true});let r=await P.readdir(e,{withFileTypes:true});for(let s of r){let n=f.join(e,s.name),i=f.join(t,s.name);if(s.isDirectory()){await P.cp(n,i,{recursive:true,force:true});continue}await P.copyFile(n,i);}}async function Ut(e,t){let r=Object.entries(e.postCrawlCopyMap||{}).map(([n,i])=>({sourcePath:n.trim(),prefix:String(i||"").trim()}));if(r.length===0)return;let s=0;t.mark("copy-extra-paths");for(let n of r){let i=n.sourcePath,a=n.prefix;if(!i||!a){t.warn("Skipped post-crawl copy mapping with empty key/value",{sourcePath:i,prefixPath:a});continue}let o=ft(i);if(!o.resolvedPath){t.warn("Skipped post-crawl copy source because alias root is not configured",{sourcePath:i,alias:o.alias||"",requiredEnv:o.requiredEnv||"",prefixPath:a});continue}let l=o.resolvedPath,u=Y(a);if(!u){t.warn("Skipped post-crawl copy mapping with invalid export prefix",{sourcePath:i,prefixPath:a});continue}let c;try{c=await P.stat(l);}catch{t.warn("Skipped post-crawl copy source because it does not exist",{sourcePath:i,sourceAbs:l,prefixPath:a});continue}let d=f.resolve(e.outputDir,u);if(c.isDirectory()){await Ft(l,d),s++,t.progress(`Copied static directory to export: ${l} -> /${u}`,{phase:"copy-extra-paths",sourcePath:l,targetPath:`/${u}`,copiedItems:s});continue}let g=f.basename(l),m=a.endsWith("/")?f.join(d,g):d;await P.mkdir(f.dirname(m),{recursive:true}),await P.copyFile(l,m),s++,t.progress(`Copied static file to export: ${l} -> /${Y(a.endsWith("/")?`${a}${g}`:a)}`,{phase:"copy-extra-paths",sourcePath:l,targetPath:`/${Y(a.endsWith("/")?`${a}${g}`:a)}`,copiedItems:s});}t.endMark("copy-extra-paths",{mappedSources:r.length,copiedItems:s});}function j(e,t,r,s,n){let i=new Set,a=L(r).replace(/\\\//g,"/");for(let u of Ot(a)){let c=v(e,u,t,s,`${n}:css-url`);c&&y(e,c)&&i.add(c.toString());}for(let u of a.matchAll(/@import\s+(?:url\()?\s*['"]?([^'"\s;]+)['"]?\s*\)?/gi)){let c=v(e,u[1],t,s,`${n}:css-import`);c&&y(e,c)&&i.add(c.toString());}for(let u of a.matchAll(/<\?xml-stylesheet[^>]+href=["']([^"']+)["'][^>]*\?>/gi)){let c=v(e,u[1],t,s,`${n}:xml-stylesheet`);c&&y(e,c)&&i.add(c.toString());}let o="(?:css|js|mjs|json|map|xml|xsl|rss|atom|txt|enc|jws|png|jpe?g|gif|webp|avif|svg|ico|woff2?|ttf|otf|eot|pdf|mp4|webm)",l=new RegExp(`(?:https?:)?//[^\\s'"<>\\);]+\\.${o}(?:\\?[^\\s'"<>\\);]*)?|(?<!\\.)/[^\\s'"<>\\);]+\\.${o}(?:\\?[^\\s'"<>\\);]*)?`,"gi");for(let u of a.matchAll(l)){let c=u[0].startsWith("//")?`${new URL(e.sourceOrigin).protocol}${u[0]}`:u[0];if(Oe(c)){s.ignore(`${n}:serialized-url`,c,"looks like JavaScript/code fragment",t);continue}let d=v(e,c,t,s,`${n}:serialized-url`);d&&y(e,d)?i.add(d.toString()):d&&s.ignore("asset",d.toString(),"serialized URL did not pass safe asset path filter",t);}return [...i]}function Qt(e,t,r,s){let n=new Set,i=L(r);for(let a of i.matchAll(/href=["']([^"'#\s][^"']*?)["']/gi)){let o=a[1],l=v(e,o,t,s,"page-link");l&&(De(e,l)||D(l)&&!H(l)&&!y(e,l)&&n.add(l.toString()));}return [...n]}async function Le(e,t,r,s){for(;r.sitemapQueue.length>0;){let n=r.sitemapQueue.shift();if(r.queuedSitemaps.delete(n),!r.doneSitemaps.has(n)){r.doneSitemaps.add(n),s.sitemap(`Fetching sitemap ${n}`,{url:n});try{let i=await t.get(n,{timeout:6e4});if(!i.ok()){s.skip("sitemap",n,i.status());continue}let a=await i.body(),o=i.url()||n;await ee(e,r.assetMap,o,a,r,s);let l=a.toString("utf8");for(let u of j(e,o,l,s,"sitemap"))k(e,r,u,s,o);for(let u of jt(l)){let c=v(e,u.loc,o,s,"sitemap-loc");c&&(J(c)?ie(e,r,c.toString(),s,o):y(e,c)?k(e,r,c.toString(),s,o):D(c)?(u.lastmod&&(r.sitemapLastmodByPage[c.toString()]=u.lastmod),W(e,r,c.toString(),s,o)):s.reject("sitemap-loc",c.toString(),"not page/sitemap/asset-like",o));}}catch(i){s.error(`Failed sitemap ${n}`,{url:n,error:String(i)});}}}}async function qt(e,t,r,s,n){let i=v(e,s,e.sourceOrigin,n,"asset-fetch");if(!i||!y(e,i))return;let a=i.toString();if(!r.doneAssets.has(a)){n.asset(`Fetching asset ${a}`,{url:a});try{let o=N(e,r.assetMap,a),l=o?await Ue(o):null;if(o&&l)try{let m=await t.head(a,{timeout:6e4}),h=m.headers();if(m.ok()&&It(l,h)){let p=await Z(o);if(p!==null){let w=m.url()||a;if(await ee(e,r.assetMap,w,p,r,n,h),r.doneAssets.add(a),n.info(`Reused existing asset ${a} based on response headers.`,{url:a,filePath:o,lastModified:h["last-modified"]||"",contentLength:h["content-length"]||"",phase:"download-assets"}),ke(r,n),Ce(i,h)){let S=p.toString("utf8");for(let b of j(e,w,S,n,"asset:cached"))k(e,r,b,n,w);}return}}}catch{}let u=await t.get(a,{timeout:6e4});if(!u.ok()){n.skip("asset",a,u.status());return}let c=await u.body(),d=u.url()||a,g=u.headers();if(await ee(e,r.assetMap,d,c,r,n,g),r.doneAssets.add(a),ke(r,n),Ce(i,g)){let m=c.toString("utf8");for(let h of j(e,d,m,n,`asset:${f.extname(i.pathname).toLowerCase()||g["content-type"]||"unknown"}`))k(e,r,h,n,d);}}catch(o){n.error(`Failed asset ${a}`,{url:a,error:String(o)});}}}async function Nt(e,t,r,s){let n=Math.max(1,Number(e.assetDownloadConcurrency||e.concurrency||1)),i=Array.from({length:n},async()=>{for(;r.assetQueue.length>0;){let a=r.assetQueue.shift();a&&(r.queuedAssets.delete(a),await qt(e,t,r,a,s));}});await Promise.all(i);}async function _t(e){await e.evaluate(async()=>{await new Promise(t=>{let r=0,s=700,n=window.setInterval(()=>{window.scrollBy(0,s),r+=s,r>=document.body.scrollHeight+window.innerHeight&&(window.clearInterval(n),window.scrollTo(0,0),t());},120);});});}async function Wt(e,t){await t.waitForLoadState("domcontentloaded",{timeout:e.readiness.timeoutMs}).catch(()=>{}),e.readiness.waitForSelector&&await t.waitForSelector(e.readiness.waitForSelector,{timeout:e.readiness.timeoutMs}).catch(()=>{}),e.readiness.waitForFunction&&await t.waitForFunction(e.readiness.waitForFunction,void 0,{timeout:e.readiness.timeoutMs}).catch(()=>{}),await _t(t),await t.waitForTimeout(e.readiness.fallbackWaitMs);}async function Ht(e,t){await t.waitForLoadState("domcontentloaded",{timeout:e.readiness.timeoutMs}).catch(()=>{}),await t.waitForLoadState("load",{timeout:e.readiness.timeoutMs}).catch(()=>{}),e.readiness.waitForSelector&&await t.waitForSelector(e.readiness.waitForSelector,{timeout:e.readiness.timeoutMs}).catch(()=>{}),await t.waitForTimeout(e.readiness.fallbackWaitMs);}function Jt(e,t){return e.noJavaScriptRenderPathPrefixes.some(r=>re(t,r))}async function Bt(e,t,r,s,n=true){let i=new Set;if(n){let o=await t.evaluate(()=>{let l=new Set,u=["href","src","poster","data-src","data-lazy-src","data-original","data-bg","data-background","data-href"],c=new Set,d=["srcset","data-srcset","data-lazy-srcset"];return document.querySelectorAll("*").forEach(g=>{for(let h of u){let p=g.getAttribute(h);p&&l.add(p);}for(let h of d){let p=g.getAttribute(h);p&&p.split(",").forEach(w=>{let S=w.trim().split(/\s+/)[0];S&&l.add(S);});}let m=g.getAttribute("style");m&&c.add(m);}),{attrs:[...l],styles:[...c]}});for(let l of o.attrs)for(let u of Dt(l)){let c=v(e,u,r,s,"dom-attr");c&&y(e,c)&&i.add(c.toString());}for(let l of o.styles)for(let u of j(e,r,l,s,"dom-style"))i.add(u);}let a=await t.content();for(let o of j(e,r,a,s,"page-html"))i.add(o);return [...i]}async function Me(e,t,r,s,n){let i=se(e,t);await V(i),await P.writeFile(i,r,"utf8"),s.stats.pagesSaved++,n&&(s.stats.pagesSaved%10===0||s.stats.pagesSaved===1)&&n.progress(`Page progress: rendered ${s.stats.pagesRendered}, saved ${s.stats.pagesSaved}, discovered ${s.stats.pagesQueued}.`,{pagesRendered:s.stats.pagesRendered,pagesSaved:s.stats.pagesSaved,pagesQueued:s.stats.pagesQueued,pageQueue:s.pageQueue.length,phase:"save-pages"});}async function zt(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 s=new URL(String(r),window.location.href),n=new URL(window.location.href);return s.hash="",n.hash="",s.href===n.href}catch{return false}};try{let r=window.location.assign.bind(window.location),s=window.location.replace.bind(window.location),n=window.location.reload.bind(window.location);Object.defineProperty(window.location,"assign",{configurable:!0,value:i=>{if(t(i)){console.warn("[smartcloud-static-publisher] blocked same-page location.assign",i??"");return}return r(i)}}),Object.defineProperty(window.location,"replace",{configurable:!0,value:i=>{if(t(i)){console.warn("[smartcloud-static-publisher] blocked same-page location.replace",i??"");return}return s(i)}}),Object.defineProperty(window.location,"reload",{configurable:!0,value:()=>{console.warn("[smartcloud-static-publisher] blocked location.reload");}});}catch{}});}async function Gt(e,t,r,s,n,i){let a=await chromium.launch({headless:true}),o=await a.newContext({viewport:e.viewport,userAgent:"WPSuiteStaticPublisher/0.8 Playwright SitemapOnly",ignoreHTTPSErrors:e.ignoreHttpsErrors}),l=await a.newContext({viewport:e.viewport,userAgent:"WPSuiteStaticPublisher/0.8 Playwright SitemapOnly",javaScriptEnabled:false,ignoreHTTPSErrors:e.ignoreHttpsErrors});await zt(o);try{for(;t.pageQueue.length>0&&!(e.maxPages>0&&t.donePages.size>=e.maxPages);){let u=t.pageQueue.shift();if(!u)break;if(t.queuedPages.delete(u),t.donePages.has(u))continue;t.donePages.add(u),i.enabled&&i.seenPages.add(u);let c=new URL(u),d=Jt(e,c.pathname);if(!D(c)||y(e,c)||H(c)){if(y(e,c)){r.info(`Worker redirected non-page URL to asset queue: ${u}`,{url:u,source:"worker-guard"}),k(e,t,u,r,"worker-guard");continue}r.reject("page",u,"guard rejected non-page URL before rendering","worker");continue}let g=await St(e,n,t,i,u,r);if(i.enabled&&s!=="single-url"){let p=i.manifest.pages[u];if(g.action==="reuse"&&p){for(let w of p.discoveredAssets)k(e,t,w,r,u);for(let w of p.discoveredPages)W(e,t,w,r,u);p.lastSeenRunId=i.runId,p.sitemapLastmod=q(t.sitemapLastmodByPage[u])??p.sitemapLastmod,g.changeToken?.supported&&(p.changeToken=g.changeToken.token,p.tokenSource=g.changeToken.tokenSource??p.tokenSource),r.info(`Incremental reuse skipped unchanged page ${u}`,{url:u,mode:"incremental",reason:g.changeToken?.supported===!0?"change-token-match":"sitemap-lastmod-match"});continue}}let m=await(d?l.newPage():o.newPage()),h=!1;await m.route("**/*",async p=>{let w=p.request();if(w.isNavigationRequest()&&w.frame()===m.mainFrame())try{let S=new URL(w.url()),b=new URL(u);if(S.hash="",b.hash="",S.href===b.href){if(h){r.warn(`Blocked same-page navigation/reload for ${u}`,{url:u,requestUrl:w.url()}),await p.abort("aborted");return}h=!0;}}catch{}await p.continue();}),m.on("response",p=>{try{let w=v(e,p.url(),e.sourceOrigin,void 0,"network-response");w&&y(e,w)&&k(e,t,w.toString(),r,u);}catch{}}),r.page(`Rendering ${u}`,{url:u});try{let p=null,w=null;try{if(p=await m.goto(u,{waitUntil:"domcontentloaded",timeout:e.navigationTimeoutMs}),p&&p.ok())try{w=await p.text();}catch{w=null;}}catch(I){r.warn(`Navigation issue for ${u}; saving current DOM if available`,{url:u,error:String(I)});}if(p&&!p.ok()){r.skip("page",u,p.status()),await m.close();continue}d?(r.info(`Rendering without JS execution for ${u}`,{url:u,mode:"no-js"}),await Ht(e,m)):await Wt(e,m),t.stats.pagesRendered++,(t.stats.pagesRendered%5===0||t.stats.pagesRendered===1||t.stats.pagesRendered===t.stats.pagesQueued)&&r.progress(`Render progress: rendered ${t.stats.pagesRendered}, saved ${t.stats.pagesSaved}, discovered ${t.stats.pagesQueued}.`,{pagesRendered:t.stats.pagesRendered,pagesSaved:t.stats.pagesSaved,pagesQueued:t.stats.pagesQueued,phase:"render-pages"});let S=await Bt(e,m,u,r,!d);for(let I of S)k(e,t,I,r,u);let b=w??await m.content(),ae=s!=="single-url"?Qt(e,u,b,r):[];if(s!=="single-url")for(let I of ae)W(e,t,I,r,u);await Me(e,u,b,t,r),i.enabled&&(i.manifest.pages[u]={url:u,outputPath:se(e,u),changeToken:g.changeToken?.supported?g.changeToken.token:null,tokenSource:g.changeToken?.supported?g.changeToken.tokenSource??null:null,sitemapLastmod:q(t.sitemapLastmodByPage[u])??null,discoveredPages:ae,discoveredAssets:S,lastCrawledAt:new Date().toISOString(),lastSeenRunId:i.runId});}catch(p){try{let w=await m.content();w&&w.trim()&&(await Me(e,u,w,t,r),r.warn(`Saved partial DOM for ${u}`,{url:u,error:String(p)}));}catch(w){r.error(`Could not save partial DOM for ${u}`,{url:u,error:String(w)});}r.error(`Failed page ${u}`,{url:u,error:String(p)});}finally{await m.close();}}}finally{await l.close(),await o.close(),await a.close();}}async function Te(e){try{let r=(await P.readFile(e,"utf8")).trim();if(!r)return [];try{let s=JSON.parse(r);if(Array.isArray(s))return s;if(s&&typeof s=="object")return [s]}catch{return r.split(/\r?\n/).map(s=>s.trim()).filter(Boolean).map(s=>{try{let n=JSON.parse(s);return n&&typeof n=="object"?n:null}catch{return null}}).filter(s=>s!==null)}return []}catch{return []}}function Ae(e){let t=String(e||"").toLowerCase();return t.includes("timeout")||t.includes("timed out")||t.includes("navigation timeout")}function Vt(e){let t=String(e?.job?.command||"").trim(),r=String(e?.job?.crawlMode||"full").trim();return t!=="publish"&&t!=="crawl"?false:r!=="incremental"}function Kt(e){let t=[e?.archivedAt,e?.job?.endedAt,e?.job?.startedAt];for(let r of t){let s=Date.parse(String(r||""));if(Number.isFinite(s))return s}return 0}async function Xt(e){try{let t=await P.readFile(e,"utf8"),r=JSON.parse(t);return r&&typeof r=="object"&&!Array.isArray(r)?r:null}catch{return null}}async function Ee(e){let t=await Te(f.join(e,"errors.jsonl"));return t.length>0?t:Te(f.join(e,"errors.json"))}async function Yt(e){let t=f.join(e.logDir,"archive"),r;try{r=await P.readdir(t);}catch{return ""}let s="",n=0;for(let i of r){let a=f.join(t,i);if(!(await P.stat(a).catch(()=>null))?.isDirectory())continue;let l=await Xt(f.join(a,"job.json"));if(!Vt(l))continue;let u=Kt(l);u>=n&&(n=u,s=a);}return s}async function Zt(e){let t=await Yt(e),r=t!==""?await Ee(t):await Ee(e.logDir),s=new Set;for(let n of r)if(Ae(n.error)||Ae(n.message)){let i=n.url;typeof i=="string"&&i&&s.add(i);}return [...s]}async function er(e){let t=[];for(let r of e)if(r.startsWith("@")){let s=r.slice(1),n=await P.readFile(s,"utf8");t.push(...n.split(/\r?\n/).map(i=>i.trim()).filter(i=>i&&!i.startsWith("#")));}else t.push(r);return t}async function tr(e){try{let t=await P.readFile(f.join(e.outputDir,"asset-map.json"),"utf8");return JSON.parse(t)}catch{return {}}}async function rr(e){let t=["crawl.log.jsonl","current-crawl-event.json","rejected.jsonl","ignored.jsonl","skipped-http.jsonl","errors.jsonl","timings.jsonl","rejected.json","ignored.json","skipped-http.json","errors.json","timings.json"];await P.mkdir(e,{recursive:true}),await Promise.all(t.map(async r=>{try{await P.unlink(f.join(e,r));}catch(s){if(s.code!=="ENOENT")throw s}}));}function Ie(e,t,r,s,n){let i=v(e,r,e.sourceOrigin,s,"manual-url");i&&(J(i)?ie(e,t,i.toString(),s,n):y(e,i)?k(e,t,i.toString(),s,n):D(i)?W(e,t,i.toString(),s,n):s.reject("manual-url",i.toString(),"not page/sitemap/asset-like",n));}async function sr(){let e=await G(),t=gt(process.argv.slice(2)),r=[];t.mode==="retry-timeouts"&&(r=await Zt(e)),t.mode==="single-url"&&(r=await er(t.urls));let s=t.crawlMode==="incremental"&&t.mode==="full",n=s?await wt(e):{schemaVersion:1,updatedAt:"",pages:{}},i={enabled:s,manifest:Se(n),previousManifest:Se(n),runId:`${Date.now()}`,seenPages:new Set,changeTokenCache:new Map};await rr(e.logDir);let a={pageQueue:[],queuedPages:new Set,donePages:new Set,assetQueue:[],queuedAssets:new Set,doneAssets:new Set,sitemapQueue:[],queuedSitemaps:new Set,doneSitemaps:new Set,sitemapLastmodByPage:{},assetMap:t.preserveOutput?await tr(e):{},stats:{pagesQueued:0,pagesRendered:0,assetsQueued:0,sitemapsQueued:0,assetsSaved:0,pagesSaved:0}};t.preserveOutput||await P.rm(e.outputDir,{recursive:true,force:true}),await P.mkdir(e.outputDir,{recursive:true});let o=new $(e.logDir,e.logLevel),l=await chromium.launch({headless:true}),u=await l.newContext({ignoreHTTPSErrors:e.ignoreHttpsErrors}),c=u.request;try{if(o.mark("discovery"),t.mode==="full"?(e.sitemapPaths.forEach(p=>ie(e,a,p,o)),await Le(e,c,a,o),e.seedPaths.forEach(p=>Ie(e,a,p,o,"seed-path"))):(r.forEach(p=>Ie(e,a,p,o,"cli")),await Le(e,c,a,o)),o.endMark("discovery",{pages:a.pageQueue.length,assets:a.assetQueue.length,sitemaps:a.sitemapQueue.length}),o.summary(`Queued ${a.pageQueue.length} pages, ${a.assetQueue.length} assets, ${a.sitemapQueue.length} sitemaps.`,{mode:t.mode,crawlMode:t.crawlMode,queuedPages:a.pageQueue.length,queuedAssets:a.assetQueue.length,queuedSitemaps:a.sitemapQueue.length}),t.mode==="retry-timeouts"&&a.pageQueue.length===0&&a.assetQueue.length===0&&a.sitemapQueue.length===0){o.summary("No timed-out URLs were queued. Skipping retry crawl.",{mode:t.mode,crawlMode:t.crawlMode,queuedPages:0,queuedAssets:0,queuedSitemaps:0}),await o.flush();return}o.mark("render-pages");let h=Array.from({length:Math.max(1,e.concurrency)},()=>Gt(e,a,o,t.mode,c,i));await Promise.all(h),o.endMark("render-pages",{pages:a.donePages.size}),o.mark("download-assets"),await Nt(e,c,a,o),o.endMark("download-assets",{assets:a.doneAssets.size}),i.enabled&&e.maxPages===0&&await vt(e,a,i,o),t.mode==="full"&&await Ut(e,o);}finally{await u.close(),await l.close();}i.enabled&&(i.manifest.updatedAt=new Date().toISOString(),await Pt(e,i.manifest)),await P.writeFile(f.join(e.outputDir,"asset-map.json"),JSON.stringify(a.assetMap,null,2),"utf8");let d=t.mode==="single-url"?xt(e,a):void 0;o.mark("rewrite-text");let g=t.mode==="single-url"?`Rewriting text files touched by URL crawl: 0/${d?.length??0}`:"Rewriting text files...";o.progress(g,{phase:"rewrite-text",index:0,totalFiles:d?.length,changedTextFiles:0}),await xe(g);let m=await fe(e,a.assetMap,{files:d,onProgress:async({index:h,totalFiles:p,changedTextFiles:w,file:S})=>{if(o.checkpoint(`Rewriting text file ${h}/${p}`,{phase:"rewrite-text",index:h,totalFiles:p,changedTextFiles:w,file:S}),h!==1&&h!==p&&h%25!==0)return;let b=`Rewriting text files: ${h}/${p}`;o.progress(b,{phase:"rewrite-text",index:h,totalFiles:p,changedTextFiles:w,file:S}),await xe(b);}});o.endMark("rewrite-text",{changedTextFiles:m}),o.summary(`Done. Rendered ${a.stats.pagesRendered} pages, processed ${a.doneSitemaps.size} sitemaps, downloaded ${a.doneAssets.size} assets.`,{mode:t.mode,crawlMode:t.crawlMode,...a.stats,pagesRendered:a.stats.pagesRendered,donePages:a.donePages.size,doneSitemaps:a.doneSitemaps.size,doneAssets:a.doneAssets.size,changedTextFiles:m}),await o.flush();}sr().catch(async e=>{console.error(e);try{let r=(await G().catch(()=>null))?.logDir??"logs",s=new $(r,"debug");s.error(`Unhandled error: ${e instanceof Error?e.message:String(e)}`,e instanceof Error?{stack:e.stack}:void 0),await s.flush();}catch{}process.exit(1);});
|
package/dist/deploy.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import {S3Client,ListObjectsV2Command,HeadObjectCommand,GetObjectCommand,DeleteObjectsCommand}from'@aws-sdk/client-s3';import {Upload}from'@aws-sdk/lib-storage';import {spawn}from'child_process';import {createHash}from'crypto';import Ae from'fast-glob';import $ from'fs/promises';import v from'path';import rt from'mime-types';function ne(t){let r=t.trim();return r==="."?".":r.replace(/\/$/,"")}function K(t){return !t||typeof t!="object"?{}:Object.fromEntries(Object.entries(t).map(([r,e])=>[r.trim(),String(e??"")]).filter(([r])=>r.length>0))}function Ie(t){if(!t||typeof t!="object")return {};let r={};for(let[e,n]of Object.entries(t)){let s=e.trim();if(!s||!n||typeof n!="object")continue;let i=n,o={};if(typeof i.targetOrigin=="string"){let a=ne(i.targetOrigin);a&&(o.targetOrigin=a);}let l=K(i.extraReplacements);if(Object.keys(l).length>0&&(o.extraReplacements=l),i.s3&&typeof i.s3=="object"){let a={},g=i.s3;typeof g.bucket=="string"&&(a.bucket=g.bucket.trim()),typeof g.prefix=="string"&&(a.prefix=g.prefix.trim()),typeof g.region=="string"&&(a.region=g.region.trim()),typeof g.htmlCacheControl=="string"&&(a.htmlCacheControl=g.htmlCacheControl.trim()),typeof g.assetCacheControl=="string"&&(a.assetCacheControl=g.assetCacheControl.trim()),Object.keys(a).length>0&&(o.s3=a);}if(i.cloudFront&&typeof i.cloudFront=="object"){let a={},g=i.cloudFront;typeof g.distributionId=="string"&&(a.distributionId=g.distributionId.trim()),Array.isArray(g.invalidationPaths)&&(a.invalidationPaths=g.invalidationPaths.map(u=>String(u??"").trim()).filter(u=>u.length>0)),Object.keys(a).length>0&&(o.cloudFront=a);}r[s]=o;}return r}function q(t,r){let n=String(t||"").replace(/\\/g,"/").trim().replace(/^\/+|\/+$/g,"");if(!n)return r;let s=n.split("/").map(i=>i.trim()).filter(i=>i.length>0&&i!=="."&&i!=="..");return s.length>0?s.join("/"):r}function je(){let t=process.env.STATIC_PUBLISHER_RUNTIME_DIR||process.env.WPSUITE_STATIC_PUBLISHER_RUNTIME_DIR||"";return t.trim()?v.resolve(t):""}function re(t,r,e){let n=String(r||"").trim();return n&&v.isAbsolute(n)?v.resolve(n):v.resolve(t,q(n,e))}async function Q(){let t=process.env.PUBLISHER_CONFIG||"publisher.config.json",r=await $.readFile(t,"utf8"),e=JSON.parse(r);e.sourceOrigin=e.sourceOrigin.replace(/\/$/,""),e.targetOrigin=ne(e.targetOrigin),e.ignoreHttpsErrors??=false,e.outputDir=String(e.outputDir||"export").trim()||"export",e.urlRewriteMode||=e.targetOrigin==="."?"relative":"absolute",e.noJavaScriptRenderPathPrefixes||=[],e.seedPaths||=[],e.sitemapPaths||=["/sitemap_index.xml","/sitemap.xml"],e.allowedAssetHosts||=[],e.assetPathPrefixes||=["/wp-content/","/wp-includes/","/static/","/assets/","/build/","/_next/","/docs/","/sitemap","/robots.txt","/llms.txt"],e.blockedPathPrefixes||=[],e.blockedSearchFragments||=[],e.concurrency||=1,e.maxPages||=0,e.extraReplacements=K(e.extraReplacements),e.postCrawlCopyMap=K(e.postCrawlCopyMap),e.logDir=String(e.logDir||"logs").trim()||"logs",e.verbose??=false,e.logLevel||=e.verbose?"debug":"info",e.s3SyncMode||="sdk-upload-delete",e.readiness||={waitForSelector:null,waitForFunction:null,timeoutMs:1500,fallbackWaitMs:1500},e.readiness.timeoutMs??=1500,e.readiness.fallbackWaitMs??=1500,e.viewport||={width:1440,height:1200},e.navigationTimeoutMs||=3e4,e.scheduler||={enabled:false,timezone:"UTC",rules:[]},e.scheduler.enabled??=false,e.scheduler.timezone||="UTC",e.scheduler.rules||=[],e.deploymentProfiles=Ie(e.deploymentProfiles),e.assetDownloadConcurrency=Number(e.assetDownloadConcurrency)>0?Number(e.assetDownloadConcurrency):e.concurrency,e.rewriteConcurrency=Number(e.rewriteConcurrency)>0?Number(e.rewriteConcurrency):e.assetDownloadConcurrency,e.defaultDeploymentProfile=String(e.defaultDeploymentProfile??"").trim(),e.defaultDeploymentProfile&&!e.deploymentProfiles[e.defaultDeploymentProfile]&&(e.defaultDeploymentProfile="");let n=je(),s=n?v.resolve(n,".."):"";return s?(e.outputDir=re(s,e.outputDir,"export"),e.logDir=re(s,e.logDir,"logs")):(v.isAbsolute(e.outputDir)||(e.outputDir=q(e.outputDir,"export")),v.isAbsolute(e.logDir)||(e.logDir=q(e.logDir,"logs"))),e}function ie(t){let r="";for(let n=0;n<t.length;n++){let s=t[n];if(s==="--profile"){let i=(t[n+1]||"").trim();if(!i)throw new Error("Missing value for --profile.");r=i,n+=1;}else if(s.startsWith("--profile=")){let i=s.slice(10).trim();if(!i)throw new Error("Missing value for --profile.");r=i;}}let e=String(process.env.PUBLISHER_DEPLOY_PROFILE||process.env.PUBLISHER_DEPLOYMENT_PROFILE||"").trim();return r||e||null}function Fe(t,r){return {...t,...r??{}}}function Me(t,r){return {...t,...r??{},invalidationPaths:[...(r?.invalidationPaths??t.invalidationPaths)||[]]}}function se(t,r){let e=String(r??t.defaultDeploymentProfile??"").trim();if(!e)return {name:null,profile:null,config:t};let n=t.deploymentProfiles?.[e];if(!n)throw new Error(`Unknown deployment profile "${e}". Check deploymentProfiles in publisher.config.json.`);return {name:e,profile:n,config:{...t,targetOrigin:n.targetOrigin??t.targetOrigin,s3:Fe(t.s3,n.s3),cloudFront:Me(t.cloudFront,n.cloudFront)}}}var oe={error:0,warn:1,info:2,debug:3},ae=process.env.STATIC_PUBLISHER_RUNTIME_DIR||process.env.WPSUITE_STATIC_PUBLISHER_RUNTIME_DIR||"",z=ae?v.join(ae,"current-progress.json"):"",le=Promise.resolve();function ue(t){return t.includes("crawl")?"crawl":t.includes("deploy")?"deploy":t.includes("invalidate")?"invalidate":t}function Oe(t,r){let e={...t},n=new Set(["pagesQueued","pagesRendered","assetsQueued","sitemapsQueued","pagesDiscovered","assetsDiscovered","sitemapsDiscovered","pagesSaved","assetsSaved","donePages","doneAssets","doneSitemaps","changedTextFiles","uploaded","failed","index","totalFiles","pageQueue","assetQueue","sitemapQueue"]);for(let[s,i]of Object.entries(r)){let o=e[s];if(typeof o=="number"&&typeof i=="number"&&n.has(s)){e[s]=Math.max(o,i);continue}e[s]=i;}return e}function _e(t,r,e){z&&(le=le.then(async()=>{let n=new Date,s=n.toISOString();await $.mkdir(v.dirname(z),{recursive:true});let i=await $.readFile(z,"utf8").then(w=>JSON.parse(w)).catch(()=>null),o=i&&typeof i.details=="object"&&i.details?i.details:{},l=ue(t),a=i?.currentStep||ue(i?.source||""),g=i?.startedAt||s,u={...i?.stepDurationsSec??{}},c=i?.stepStartedAt||s;if(a&&a!==l&&i?.stepStartedAt){let w=Math.max(0,Math.round((n.getTime()-new Date(i.stepStartedAt).getTime())/1e3));u[a]=(u[a]??0)+w,c=s;}let m=Math.max(0,Math.round((n.getTime()-new Date(c).getTime())/1e3)),p=Math.max(0,Math.round((n.getTime()-new Date(g).getTime())/1e3)),R={checkedAt:s,source:t,message:r,details:Oe(o,e??{}),startedAt:g,currentStep:l,stepStartedAt:c,stepElapsedSec:m,totalElapsedSec:p,stepDurationsSec:u};await $.writeFile(z,JSON.stringify(R,null,2),"utf8");}).catch(()=>{}));}async function Te(t,r){await $.mkdir(v.dirname(t),{recursive:true}),await $.appendFile(t,`${JSON.stringify(r)}
|
|
2
|
+
`,"utf8");}var M=class{constructor(r,e,n="info"){this.logDir=r;this.logFile=e;this.level=n;this.ensureLogFileReady();}logDir;logFile;level;startedAt=Date.now();marks=new Map;initPromise=null;writeQueue=Promise.resolve();writeError=null;get logPath(){return v.join(this.logDir,this.logFile)}get errorsPath(){return v.join(this.logDir,this.logFile.replace(".log.jsonl",".errors.jsonl"))}ensureLogFileReady(){return this.initPromise||(this.initPromise=(async()=>{await $.mkdir(this.logDir,{recursive:true}),await $.writeFile(this.logPath,"","utf8");})()),this.initPromise}enqueueTask(r){this.writeQueue=this.writeQueue.then(r).catch(e=>{this.writeError=e instanceof Error?e:new Error(String(e));});}enqueueLine(r){this.enqueueTask(async()=>{await this.ensureLogFileReady(),await $.appendFile(this.logPath,`${r}
|
|
3
|
+
`,"utf8");});}enqueueJsonLine(r,e){this.enqueueTask(()=>Te(r,e));}accepts(r){return oe[r]<=oe[this.level]}push(r,e,n){if(!this.accepts(r))return;let s=JSON.stringify({time:new Date().toISOString(),level:r,message:e,...n||{}});this.enqueueLine(s),r==="error"?console.error(`[ERROR] ${e}`,n?JSON.stringify(n):""):r==="warn"?console.warn(`[WARN] ${e}`,n?JSON.stringify(n):""):console.log(`[${r.toUpperCase().padEnd(5)}] ${e}`);}info(r,e){this.push("info",r,e);}progress(r,e){_e(this.logFile,r,e);}debug(r,e){this.push("debug",r,e);}warn(r,e){this.push("warn",r,e);}error(r,e){this.enqueueJsonLine(this.errorsPath,{message:r,...e||{}}),this.push("error",r,e);}mark(r){this.marks.set(r,Date.now());}endMark(r,e){let n=this.marks.get(r);if(!n)return;let s=Date.now()-n,i=Number((s/1e3).toFixed(2));this.push("info",`Timing ${r}: ${i}s`,{name:r,ms:s,seconds:i,...e||{}});}async flush(){let r=Date.now()-this.startedAt,e=Number((r/1e3).toFixed(2));if(this.push("info",`Total time: ${e}s`,{name:"total",ms:r,seconds:e}),await this.writeQueue,this.writeError)throw this.writeError}};function ge(t){return [".html",".htm",".css",".js",".mjs",".json",".xml",".xsl",".txt",".svg",".map",".enc",".jws"].includes(v.extname(t).toLowerCase())}function pe(t){return t.replace(/"/g,'"').replace(/"/g,'"').replace(/'/g,"'").replace(/'/g,"'").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function E(t){return t.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">")}function N(t){return t.replace(/\//g,"\\/")}function ce(t){return t.replace(/\//g,"\\\\/")}function de(t,r,e){if(!r)return;t[r]=e;let n=N(r),s=N(e);t[n]=s;let i=ce(r),o=ce(e);t[i]=o;let l=E(r),a=E(e);t[l]=a;let g=E(n),u=E(s);t[g]=u;let c=E(i),m=E(o);t[c]=m;}function me(t,r){try{return new URL(t,r==="."?"https://relative.invalid":r).pathname}catch{return t.startsWith("/")?t:`/${t.replace(/^\.\//,"")}`}}function fe(t,r,e){if(!r)return e;let n=v.dirname(v.resolve(r)),s=v.resolve(t,e.replace(/^\/+/,"")),i=v.relative(n,s).replace(/\\/g,"/");return i?(i.startsWith(".")||(i=`./${i}`),i):"."}var ze=["wp-content/","wp-includes/","wp-admin/","wp-json/","_next/"],Ne=new Set([".html",".htm"]),we="WPSuite.io Static Publisher",Ue=we.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");function He(t,r,e){if(t.wpsuite?.subscriber===true||!e)return false;let n=v.extname(e).toLowerCase();return Ne.has(n)?/<head\b|<html\b|<!doctype html/i.test(r):false}function Je(t){if(new RegExp(`<meta\\b(?=[^>]*\\bname=(["'])generator\\1)(?=[^>]*\\bcontent=(["'])${Ue}\\2)[^>]*\\/?>`,"i").test(t))return t;let r=`<meta name="generator" content="${we}" />`;if(t.match(/(\r?\n)([ \t]*)<\/head>/i))return t.replace(/(\r?\n)([ \t]*)<\/head>/i,`$1$2${r}$1$2</head>`);let n=t.includes(`\r
|
|
4
|
+
`)?`\r
|
|
5
|
+
`:`
|
|
6
|
+
`;return t.replace(/<head\b[^>]*>/i,s=>`${s}${n} ${r}`)}function Be(t){let r=pe(t).trim();return r.startsWith("{")||r.startsWith("[")||r.includes("\\/")||/"@context"|"@type"/.test(r)}function We(t,r){let e=[...new Set(r)].filter(i=>i.includes("/")&&!/\\+\//.test(i)).map(i=>[i,N(i)]).filter(([i,o])=>i!==o).sort((i,o)=>o[0].length-i[0].length);if(!e.length)return t;let n=i=>{let o=i;for(let[l,a]of e)o=o.split(l).join(a);return o},s=t.replace(/(<script\b[^>]*\btype=["']application\/(?:ld\+)?json["'][^>]*>)([\s\S]*?)(<\/script>)/gi,(i,o,l,a)=>`${o}${n(l)}${a}`);return s=s.replace(/(<meta\b[^>]*\bcontent=(['"]))([\s\S]*?)(\2[^>]*>)/gi,(i,o,l,a,g)=>Be(a)?`${o}${n(a)}${g}`:i),s}function Ke(t,r){if(!r)return null;let e=v.dirname(v.resolve(r)),n=v.resolve(t.outputDir),s=v.relative(e,n).replace(/\\/g,"/");return s?(s.startsWith(".")||(s=`./${s}`),s.endsWith("/")?s:`${s}/`):"./"}function qe(t,r,e){let n=Ke(r,e);if(!n)return t;let s=t;for(let i of ze){let o=i.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),l=new RegExp(`(?:\\.{1,}/)+${o}`,"g"),a=new RegExp(`(?<!\\.)/${o}`,"g");s=s.replace(l,`${n}${i}`).replace(a,`${n}${i}`);let g=i.replace(/\//g,"\\/"),u=n.replace(/\//g,"\\/"),c=g.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),m=new RegExp(`(?:\\.{1,}\\\\/)+${c}`,"g"),p=new RegExp(`(?:\\.{1,}(?:\\\\/|\\\\))+${c}`,"g"),R=new RegExp(`(?<![\\.\\\\])\\/${c}`,"g");s=s.replace(m,`${u}${g}`).replace(p,`${u}${g}`).replace(R,`${u}${g}`);}return s}function Qe(t,r,e){if(t.urlRewriteMode==="absolute")return r;let n=(()=>{try{return new URL(r,t.targetOrigin==="."?"https://relative.invalid":t.targetOrigin)}catch{return null}})(),s=n?n.pathname:me(r,t.targetOrigin),i=n?`${n.search}${n.hash}`:"";return t.urlRewriteMode==="root-relative"?`${s}${i}`:`${fe(t.outputDir,e,s)}${i}`}function V(t,r,e,n,s){de(t,e,Qe(r,n,s));}function Ve(t,r,e,n){let s=t,i={};V(i,r,r.sourceOrigin,r.targetOrigin,n);for(let[l,a]of Object.entries(r.extraReplacements))V(i,r,l,a,n);for(let[l,a]of Object.entries(e))V(i,r,l,a,n);let o=Object.entries(i).sort((l,a)=>a[0].length-l[0].length);for(let[l,a]of o)s=s.split(l).join(a);return r.urlRewriteMode!=="absolute"&&(s=qe(s,r,n)),s=We(s,o.map(([,l])=>l)),He(r,s,n)&&(s=Je(s)),s}async function ye(t,r,e={}){let n=e.files?[...new Set(e.files)].map(u=>v.resolve(u)).filter(u=>u.startsWith(v.resolve(t.outputDir))).map(u=>v.relative(t.outputDir,u).replace(/\\/g,"/")).filter(u=>u.length>0):await Ae(["**/*"],{cwd:t.outputDir,onlyFiles:true,dot:true}),s=0;async function i(u){let c=v.join(t.outputDir,u);if(ge(c)&&u!=="asset-map.json"){let m=v.extname(c).toLowerCase(),p;try{p=await $.readFile(c,"utf8");}catch(y){if(y.code!=="ENOENT")throw y;return {changed:false}}let w=Ve(p,t,r,m===".js"||m===".mjs"?void 0:c);if(w!==p)return await $.writeFile(c,w,"utf8"),{changed:true}}return {changed:false}}let o=Math.max(1,Number(t.rewriteConcurrency||t.assetDownloadConcurrency||1)),l=0,a=0,g=Array.from({length:Math.min(o,Math.max(n.length,1))},async()=>{for(;;){let u=l;if(u>=n.length)return;l+=1;let c=n[u];(await i(c)).changed&&(s+=1),a+=1,await e.onProgress?.({index:a,totalFiles:n.length,changedTextFiles:s,file:c});}});return await Promise.all(g),s}var $e="wpsuite-sha256",xe="wpsuite-normalized-sha256",nt=/(?:^|[-_:.])(id|uid|nonce|token|hash|instance)(?:$|[-_:.])/i,ee=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;function it(t){return t&&(ee.test(t)?"__uuid__":/^[0-9a-f]{6,}$/i.test(t)?"__hex__":/^[0-9]{8,}$/.test(t)?"__num__":/^(?=.*[a-z])(?=.*\d)[a-z0-9]{8,}$/i.test(t)?"__alnum__":t)}function Z(t){return t.split(/([_:.\-/=?&#]+)/g).map((r,e)=>e%2===1?r:it(r)).join("")}function st(t){return t.split(/([_:.\-/=?&#]+)/g).map((r,e)=>e%2===1?r:ee.test(r)?"__uuid__":/^[0-9a-f]{6,}$/i.test(r)?"__hex__":/^[0-9]{6,}$/.test(r)?"__num__":/^[a-z0-9_-]{6,}$/i.test(r)?"__id__":r).join("")}function U(t,r){return Array.isArray(t)?t.map(e=>U(e,r)):t&&typeof t=="object"?Object.fromEntries(Object.entries(t).map(([e,n])=>[e,U(n,e)])):typeof t!="string"?t:r&&nt.test(r)?st(t):ee.test(t)?"__uuid__":/^[0-9a-f]{6,}$/i.test(t)||/^[0-9]{8,}$/.test(t)?Z(t):t}function ot(t){return t.replace(/((?:https?:\/\/[^"'\s>]+|\/)(?:wp-content|wp-includes)\/[^"'\s>?]+)\?ver=[A-Za-z0-9._-]+(?=$|[#"'])/gi,"$1")}function at(t,r){let e=r.trim();if((e.startsWith("{")||e.startsWith("[")||e.startsWith('"'))&&t.startsWith("data-"))try{let s=JSON.parse(e);return JSON.stringify(U(s))}catch{}if(t.startsWith("data-")&&/^[A-Za-z0-9+/=]+$/.test(e))try{let s=Buffer.from(e,"base64").toString("utf8"),i=JSON.parse(s),o=JSON.stringify(U(i));return Buffer.from(o,"utf8").toString("base64")}catch{}return r}function lt(t,r){let e=t.toLowerCase();return e==="class"?r.split(/(\s+)/).map((n,s)=>s%2===1?n:Z(n)).join(""):e==="id"||e==="for"||e==="aria-controls"||e==="aria-labelledby"||e==="aria-describedby"||e==="aria-owns"||e==="aria-activedescendant"||e==="name"?Z(r):e==="href"||e==="src"?ot(r):at(e,r)}function ut(t){return t.replace(/<([a-z][^\s/>]*)([^<>]*?)>/gi,(r,e,n)=>{if(e.startsWith("/"))return r;let s=n.replace(/\s([:@a-zA-Z_][-:.a-zA-Z0-9_]*?)=(['"])([\s\S]*?)\2/g,(i,o,l,a)=>{let g=lt(o,a);return ` ${o}=${l}${g}${l}`});return `<${e}${s}>`})}function G(t){let r=v.extname(t).toLowerCase();return [".html",".htm",".xml",".xsl",".txt"].includes(r)||["robots.txt","sitemap.xml"].includes(v.basename(t))}function be(t,r){return [t.replace(/^\/+|\/+$/g,""),r.replace(/^\/+/,"")].filter(Boolean).join("/")}async function ct(t,r,e,n){let s=new Map,i,o=0;do{o++;let l=await t.send(new ListObjectsV2Command({Bucket:r,Prefix:e||void 0,ContinuationToken:i}));for(let a of l.Contents||[]){if(!a.Key)continue;let g=(a.ETag||"").replace(/^"|"$/g,"").toLowerCase(),u=Number(a.Size??0);s.set(a.Key,{etag:g,size:u});}i=l.NextContinuationToken,n.debug(`Listed S3 keys page ${o}: ${s.size} keys so far`);}while(i);return s}async function gt(t,r,e,n,s){let i=Array.from(e).filter(o=>!n.has(o));if(i.length===0)return s.info("No stale S3 objects to delete."),[];s.info(`Deleting ${i.length} stale S3 object(s)\u2026`);for(let o=0;o<i.length;o+=1e3){let l=i.slice(o,o+1e3);if(l.length!==0){await t.send(new DeleteObjectsCommand({Bucket:r,Delete:{Objects:l.map(a=>({Key:a}))}})),s.info(`Deleted batch of ${l.length} object(s).`);for(let a of l)s.debug(` deleted: ${a}`);}}return i}function pt(t){return createHash("md5").update(t).digest("hex")}function Pe(t){return createHash("sha256").update(t).digest("hex")}function Y(t){return createHash("sha256").update(ut(t.toString("utf8"))).digest("hex")}async function dt(t,r,e,n){if(n.has(e))return n.get(e)??null;try{let o=((await t.send(new HeadObjectCommand({Bucket:r,Key:e}))).Metadata?.[$e]||"").trim().toLowerCase()||null;return n.set(e,o),o}catch{return n.set(e,null),null}}async function mt(t,r,e,n,s){if(n.has(e))return n.get(e)??null;try{let o=((await t.send(new HeadObjectCommand({Bucket:r,Key:e}))).Metadata?.[xe]||"").trim().toLowerCase();if(o)return n.set(e,o),o}catch{return n.set(e,null),null}if(s.has(e)){let i=s.get(e)??null;return n.set(e,i),i}try{let o=await(await t.send(new GetObjectCommand({Bucket:r,Key:e}))).Body?.transformToByteArray(),l=o?Y(Buffer.from(o)):null;return s.set(e,l),n.set(e,l),l}catch{return s.set(e,null),n.set(e,null),null}}function ft(t){return (t.split(`
|
|
7
|
+
`).find(e=>e.trim()!=="")??t).trim()}function ht(t){return t.replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"profile"}function wt(t,r){let e=t,n=Object.entries(r).sort((s,i)=>i[0].length-s[0].length);for(let[s,i]of n)s&&(e=e.split(s).join(i));return e}async function yt(t,r,e,n){let s=v.join(t,"asset-map.json"),i;try{i=await $.readFile(s,"utf8");}catch(a){if(a.code==="ENOENT")return;throw a}let o=JSON.parse(i);if(!o||typeof o!="object")return;let l=Object.fromEntries(Object.entries(o).map(([a,g])=>{let u=String(g??"");return r.urlRewriteMode==="absolute"&&r.targetOrigin&&e.targetOrigin&&r.targetOrigin!==e.targetOrigin&&(u=u.split(r.targetOrigin).join(e.targetOrigin)),u=wt(u,n),[a,u]}));await $.writeFile(s,JSON.stringify(l,null,2),"utf8");}async function bt(t,r,e,n,s){let i=t.targetOrigin!==r.targetOrigin,o=Object.keys(n).length>0;if(!e||!i&&!o)return {outputDir:t.outputDir,cleanup:async()=>{}};if(i&&t.urlRewriteMode!=="absolute")throw new Error(`Deployment profile "${e}" changes targetOrigin, but the base crawl output must use urlRewriteMode "absolute" for multi-target redeploys.`);let l=v.join(v.dirname(t.outputDir),`.deploy-profile-${ht(e)}-${Date.now()}`);s.info(`Preparing deployment profile "${e}" from existing crawl output...`),await $.rm(l,{recursive:true,force:true}),await $.cp(t.outputDir,l,{recursive:true});let a={...t,sourceOrigin:t.targetOrigin,targetOrigin:r.targetOrigin,extraReplacements:n,outputDir:l},g=await ye(a,{});return await yt(l,t,r,n),s.info(`Prepared deployment profile "${e}" in ${l} (${g} rewritten text file(s)).`),{outputDir:l,cleanup:async()=>{await $.rm(l,{recursive:true,force:true});}}}async function Pt(t,r,e){await new Promise((n,s)=>{let i=`${t.outputDir.replace(/\/+$/,"")}/`,o=`s3://${t.s3.bucket}/${t.s3.prefix.replace(/^\/+|\/+$/g,"")}`.replace(/\/$/,""),l=["s3"];r==="aws-s3-cp-recursive"?l.push("cp",i,o,"--recursive","--region",t.s3.region):(l.push("sync",i,o,"--region",t.s3.region),r==="aws-s3-sync-delete"&&l.push("--delete")),e.info(`Running AWS CLI: aws ${l.join(" ")}`);let a=spawn("aws",l,{stdio:["ignore","pipe","pipe"]}),g=Date.now(),u=setInterval(()=>{let c=Math.floor((Date.now()-g)/1e3);e.progress(`AWS CLI is still running (${c}s elapsed) \u2014 syncing/copying in progress...`,{mode:r,elapsedSec:c}),e.info(`AWS CLI is still running (${c}s elapsed) \u2014 syncing/copying in progress...`);},15e3);a.stdout.setEncoding("utf8"),a.stderr.setEncoding("utf8"),a.stdout.on("data",c=>{c.split(/\r?\n/).map(m=>m.trim()).filter(m=>m.length>0).forEach(m=>e.info(m));}),a.stderr.on("data",c=>{c.split(/\r?\n/).map(m=>m.trim()).filter(m=>m.length>0).forEach(m=>e.warn(m));}),a.on("error",c=>{clearInterval(u),s(new Error(`Failed to start aws CLI: ${c.message}`));}),a.on("close",c=>{if(clearInterval(u),c===0){n();return}s(new Error(`aws CLI exited with code ${c??-1}`));});});}async function vt(){let t=await Q(),r=ie(process.argv.slice(2)),e=se(t,r),n=v.resolve(e.config.logDir||"logs"),s=process.env.STATIC_PUBLISHER_RUNTIME_DIR||process.env.WPSUITE_STATIC_PUBLISHER_RUNTIME_DIR||"",i=s?v.join(v.resolve(s),"deploy-diff.json"):"",o=new M(n,"deploy.log.jsonl",e.config.logLevel??"info"),l=e.profile?.extraReplacements??{},a=async()=>{};try{let g=await bt(t,e.config,e.name,l,o);a=g.cleanup;let u={...e.config,outputDir:g.outputDir},c=u.s3SyncMode??"sdk-upload-delete";e.name&&o.info(`Using deployment profile: ${e.name}`),o.info(`Starting deploy \u2014 s3://${u.s3.bucket}/${u.s3.prefix||""} (region: ${u.s3.region})`),o.info(`S3 sync mode: ${c}`),o.mark("deploy");try{let b=await new Promise((W,d)=>{let P=spawn("aws",["--version"],{stdio:["ignore","pipe","pipe"]}),T="",f="";P.stdout.setEncoding("utf8"),P.stderr.setEncoding("utf8"),P.stdout.on("data",h=>{T+=h;}),P.stderr.on("data",h=>{f+=h;}),P.on("error",h=>d(h)),P.on("close",h=>{h===0?W(ft(T||f)):d(new Error(`aws --version exited with ${h??-1}`));});});o.debug(`AWS CLI detected: ${b}`);}catch(b){o.warn(`AWS CLI version check failed: ${b.message}`);}if(c==="aws-s3-sync-delete"||c==="aws-s3-sync"||c==="aws-s3-cp-recursive"){await Pt(u,c,o),i&&(await $.mkdir(v.dirname(i),{recursive:!0}),await $.writeFile(i,JSON.stringify({generatedAt:new Date().toISOString(),mode:c,summary:{strategy:"cli-mode",note:"Detailed object-level diff is not collected in AWS CLI modes.",...e.name?{profile:e.name}:{}},uploadedKeys:[],skippedKeys:[],failedKeys:[],deletedKeys:[]},null,2),"utf8")),o.endMark("deploy"),await o.flush();return}let m=new S3Client({region:u.s3.region}),p=await Ae(["**/*"],{cwd:u.outputDir,onlyFiles:!0,dot:!0}),R=new Set(p.map(b=>be(u.s3.prefix,b)));o.info(`Found ${p.length} file(s) in output directory "${u.outputDir}".`);let w=0,y=0,D=0,H=[],k=[],J=[],B=[],_=new Map,De=new Map,Ce=new Map,Re=new Map;(c==="sdk-upload-delete"||c==="sdk-upload-only")&&(o.info("Listing existing S3 objects to detect unchanged files..."),_=await ct(m,u.s3.bucket,u.s3.prefix,o),o.info(`Found ${_.size} existing key(s) in S3.`));for(let b of p){let W=v.join(u.outputDir,b),d=be(u.s3.prefix,b),P=rt.lookup(b)||"application/octet-stream",T=G(b)?u.s3.htmlCacheControl:u.s3.assetCacheControl,f=w+y+D+1;p.length<=200||f<=5||f%10===0||f===p.length?(o.progress(`Uploading [${f}/${p.length}] ${d} (${P})`,{file:b,key:d,index:f,totalFiles:p.length,contentType:P,mode:c}),o.info(`Uploading [${f}/${p.length}] ${d} (${P})`)):o.debug(`Uploading [${f}/${p.length}] ${d} (${P})`);try{let h=await $.readFile(W),I=_.get(d),j=null,L=null;if(I){let Le=h.byteLength;if(I.size===Le){let A=I.etag.includes("-");if(I.etag!==""&&!A){let Ee=pt(h);if(I.etag===Ee){y++,p.length<=200||y<=5||y%25===0?(o.progress(`Skipped unchanged [${f}/${p.length}] ${d}`,{file:b,key:d,index:f,totalFiles:p.length,uploaded:w,skipped:y,failed:D,mode:c,reason:"etag-md5-match"}),o.info(`Skipped unchanged [${f}/${p.length}] ${d}`)):o.debug(`Skipped unchanged [${f}/${p.length}] ${d}`),k.push(d);continue}}j=Pe(h);let te=await dt(m,u.s3.bucket,d,De);if(te&&te===j){y++,p.length<=200||y<=5||y%25===0?(o.progress(`Skipped unchanged [${f}/${p.length}] ${d}`,{file:b,key:d,index:f,totalFiles:p.length,uploaded:w,skipped:y,failed:D,mode:c,reason:"metadata-sha256-match"}),o.info(`Skipped unchanged [${f}/${p.length}] ${d}`)):o.debug(`Skipped unchanged [${f}/${p.length}] ${d}`),k.push(d);continue}}if(G(b)){L=Y(h);let A=await mt(m,u.s3.bucket,d,Ce,Re);if(A&&A===L){y++,p.length<=200||y<=5||y%25===0?(o.progress(`Skipped semantically unchanged [${f}/${p.length}] ${d}`,{file:b,key:d,index:f,totalFiles:p.length,uploaded:w,skipped:y,failed:D,mode:c,reason:"normalized-html-match"}),o.info(`Skipped semantically unchanged [${f}/${p.length}] ${d}`)):o.debug(`Skipped semantically unchanged [${f}/${p.length}] ${d}`),k.push(d);continue}}}j=j??Pe(h),L=L??(G(b)?Y(h):null),await new Upload({client:m,params:{Bucket:u.s3.bucket,Key:d,Body:h,ContentType:P,CacheControl:T,Metadata:{[$e]:j,...L?{[xe]:L}:{}}}}).done(),w++,H.push(d),(w%25===0||w===p.length)&&(o.progress(`Uploaded ${w}/${p.length} file(s)\u2026`,{uploaded:w,skipped:y,failed:D,totalFiles:p.length,mode:c}),o.info(`Uploaded ${w}/${p.length} file(s)\u2026`));}catch(h){D++,J.push(d),o.error(`Failed to upload ${d}: ${h.message}`,{key:d,error:String(h)});}}o.info(`Upload complete: ${w} uploaded, ${y} skipped unchanged, ${D} failed.`),(c==="sdk-upload-delete"||c==="sdk-upload-only")&&(c==="sdk-upload-delete"?B=await gt(m,u.s3.bucket,_.keys(),R,o):o.info("Sync mode is sdk-upload-only \u2014 skipping stale object deletion.")),i&&(await $.mkdir(v.dirname(i),{recursive:!0}),await $.writeFile(i,JSON.stringify({generatedAt:new Date().toISOString(),mode:c,summary:{totalFiles:p.length,uploaded:H.length,skipped:k.length,failed:J.length,deleted:B.length,...e.name?{profile:e.name}:{}},uploadedKeys:H,skippedKeys:k,failedKeys:J,deletedKeys:B},null,2),"utf8")),o.endMark("deploy"),await o.flush();}finally{await a().catch(()=>{});}}vt().catch(async t=>{console.error(t);try{let e=(await Q().catch(()=>null))?.logDir??"logs",n=new M(e,"deploy.log.jsonl","debug");n.error(`Unhandled error: ${t instanceof Error?t.message:String(t)}`,t instanceof Error?{stack:t.stack}:void 0),await n.flush();}catch{}process.exit(1);});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import {CloudFrontClient,CreateInvalidationCommand}from'@aws-sdk/client-cloudfront';import g from'fs/promises';import d from'path';function S(i){let t=i.trim();return t==="."?".":t.replace(/\/$/,"")}function P(i){return !i||typeof i!="object"?{}:Object.fromEntries(Object.entries(i).map(([t,e])=>[t.trim(),String(e??"")]).filter(([t])=>t.length>0))}function T(i){if(!i||typeof i!="object")return {};let t={};for(let[e,r]of Object.entries(i)){let o=e.trim();if(!o||!r||typeof r!="object")continue;let n=r,l={};if(typeof n.targetOrigin=="string"){let s=S(n.targetOrigin);s&&(l.targetOrigin=s);}let c=P(n.extraReplacements);if(Object.keys(c).length>0&&(l.extraReplacements=c),n.s3&&typeof n.s3=="object"){let s={},a=n.s3;typeof a.bucket=="string"&&(s.bucket=a.bucket.trim()),typeof a.prefix=="string"&&(s.prefix=a.prefix.trim()),typeof a.region=="string"&&(s.region=a.region.trim()),typeof a.htmlCacheControl=="string"&&(s.htmlCacheControl=a.htmlCacheControl.trim()),typeof a.assetCacheControl=="string"&&(s.assetCacheControl=a.assetCacheControl.trim()),Object.keys(s).length>0&&(l.s3=s);}if(n.cloudFront&&typeof n.cloudFront=="object"){let s={},a=n.cloudFront;typeof a.distributionId=="string"&&(s.distributionId=a.distributionId.trim()),Array.isArray(a.invalidationPaths)&&(s.invalidationPaths=a.invalidationPaths.map(u=>String(u??"").trim()).filter(u=>u.length>0)),Object.keys(s).length>0&&(l.cloudFront=s);}t[o]=l;}return t}function w(i,t){let r=String(i||"").replace(/\\/g,"/").trim().replace(/^\/+|\/+$/g,"");if(!r)return t;let o=r.split("/").map(n=>n.trim()).filter(n=>n.length>0&&n!=="."&&n!=="..");return o.length>0?o.join("/"):t}function O(){let i=process.env.STATIC_PUBLISHER_RUNTIME_DIR||process.env.WPSUITE_STATIC_PUBLISHER_RUNTIME_DIR||"";return i.trim()?d.resolve(i):""}function b(i,t,e){let r=String(t||"").trim();return r&&d.isAbsolute(r)?d.resolve(r):d.resolve(i,w(r,e))}async function y(){let i=process.env.PUBLISHER_CONFIG||"publisher.config.json",t=await g.readFile(i,"utf8"),e=JSON.parse(t);e.sourceOrigin=e.sourceOrigin.replace(/\/$/,""),e.targetOrigin=S(e.targetOrigin),e.ignoreHttpsErrors??=false,e.outputDir=String(e.outputDir||"export").trim()||"export",e.urlRewriteMode||=e.targetOrigin==="."?"relative":"absolute",e.noJavaScriptRenderPathPrefixes||=[],e.seedPaths||=[],e.sitemapPaths||=["/sitemap_index.xml","/sitemap.xml"],e.allowedAssetHosts||=[],e.assetPathPrefixes||=["/wp-content/","/wp-includes/","/static/","/assets/","/build/","/_next/","/docs/","/sitemap","/robots.txt","/llms.txt"],e.blockedPathPrefixes||=[],e.blockedSearchFragments||=[],e.concurrency||=1,e.maxPages||=0,e.extraReplacements=P(e.extraReplacements),e.postCrawlCopyMap=P(e.postCrawlCopyMap),e.logDir=String(e.logDir||"logs").trim()||"logs",e.verbose??=false,e.logLevel||=e.verbose?"debug":"info",e.s3SyncMode||="sdk-upload-delete",e.readiness||={waitForSelector:null,waitForFunction:null,timeoutMs:1500,fallbackWaitMs:1500},e.readiness.timeoutMs??=1500,e.readiness.fallbackWaitMs??=1500,e.viewport||={width:1440,height:1200},e.navigationTimeoutMs||=3e4,e.scheduler||={enabled:false,timezone:"UTC",rules:[]},e.scheduler.enabled??=false,e.scheduler.timezone||="UTC",e.scheduler.rules||=[],e.deploymentProfiles=T(e.deploymentProfiles),e.assetDownloadConcurrency=Number(e.assetDownloadConcurrency)>0?Number(e.assetDownloadConcurrency):e.concurrency,e.rewriteConcurrency=Number(e.rewriteConcurrency)>0?Number(e.rewriteConcurrency):e.assetDownloadConcurrency,e.defaultDeploymentProfile=String(e.defaultDeploymentProfile??"").trim(),e.defaultDeploymentProfile&&!e.deploymentProfiles[e.defaultDeploymentProfile]&&(e.defaultDeploymentProfile="");let r=O(),o=r?d.resolve(r,".."):"";return o?(e.outputDir=b(o,e.outputDir,"export"),e.logDir=b(o,e.logDir,"logs")):(d.isAbsolute(e.outputDir)||(e.outputDir=w(e.outputDir,"export")),d.isAbsolute(e.logDir)||(e.logDir=w(e.logDir,"logs"))),e}function D(i){let t="";for(let r=0;r<i.length;r++){let o=i[r];if(o==="--profile"){let n=(i[r+1]||"").trim();if(!n)throw new Error("Missing value for --profile.");t=n,r+=1;}else if(o.startsWith("--profile=")){let n=o.slice(10).trim();if(!n)throw new Error("Missing value for --profile.");t=n;}}let e=String(process.env.PUBLISHER_DEPLOY_PROFILE||process.env.PUBLISHER_DEPLOYMENT_PROFILE||"").trim();return t||e||null}function M(i,t){return {...i,...t??{}}}function $(i,t){return {...i,...t??{},invalidationPaths:[...(t?.invalidationPaths??i.invalidationPaths)||[]]}}function C(i,t){let e=String(t??i.defaultDeploymentProfile??"").trim();if(!e)return {name:null,profile:null,config:i};let r=i.deploymentProfiles?.[e];if(!r)throw new Error(`Unknown deployment profile "${e}". Check deploymentProfiles in publisher.config.json.`);return {name:e,profile:r,config:{...i,targetOrigin:r.targetOrigin??i.targetOrigin,s3:M(i.s3,r.s3),cloudFront:$(i.cloudFront,r.cloudFront)}}}var L={error:0,warn:1,info:2,debug:3},I=process.env.STATIC_PUBLISHER_RUNTIME_DIR||process.env.WPSUITE_STATIC_PUBLISHER_RUNTIME_DIR||"",f=I?d.join(I,"current-progress.json"):"",F=Promise.resolve();function R(i){return i.includes("crawl")?"crawl":i.includes("deploy")?"deploy":i.includes("invalidate")?"invalidate":i}function A(i,t){let e={...i},r=new Set(["pagesQueued","pagesRendered","assetsQueued","sitemapsQueued","pagesDiscovered","assetsDiscovered","sitemapsDiscovered","pagesSaved","assetsSaved","donePages","doneAssets","doneSitemaps","changedTextFiles","uploaded","failed","index","totalFiles","pageQueue","assetQueue","sitemapQueue"]);for(let[o,n]of Object.entries(t)){let l=e[o];if(typeof l=="number"&&typeof n=="number"&&r.has(o)){e[o]=Math.max(l,n);continue}e[o]=n;}return e}function N(i,t,e){f&&(F=F.then(async()=>{let r=new Date,o=r.toISOString();await g.mkdir(d.dirname(f),{recursive:true});let n=await g.readFile(f,"utf8").then(v=>JSON.parse(v)).catch(()=>null),l=n&&typeof n.details=="object"&&n.details?n.details:{},c=R(i),s=n?.currentStep||R(n?.source||""),a=n?.startedAt||o,u={...n?.stepDurationsSec??{}},p=n?.stepStartedAt||o;if(s&&s!==c&&n?.stepStartedAt){let v=Math.max(0,Math.round((r.getTime()-new Date(n.stepStartedAt).getTime())/1e3));u[s]=(u[s]??0)+v,p=o;}let k=Math.max(0,Math.round((r.getTime()-new Date(p).getTime())/1e3)),E=Math.max(0,Math.round((r.getTime()-new Date(a).getTime())/1e3)),x={checkedAt:o,source:i,message:t,details:A(l,e??{}),startedAt:a,currentStep:c,stepStartedAt:p,stepElapsedSec:k,totalElapsedSec:E,stepDurationsSec:u};await g.writeFile(f,JSON.stringify(x,null,2),"utf8");}).catch(()=>{}));}async function _(i,t){await g.mkdir(d.dirname(i),{recursive:true}),await g.appendFile(i,`${JSON.stringify(t)}
|
|
2
|
+
`,"utf8");}var h=class{constructor(t,e,r="info"){this.logDir=t;this.logFile=e;this.level=r;this.ensureLogFileReady();}logDir;logFile;level;startedAt=Date.now();marks=new Map;initPromise=null;writeQueue=Promise.resolve();writeError=null;get logPath(){return d.join(this.logDir,this.logFile)}get errorsPath(){return d.join(this.logDir,this.logFile.replace(".log.jsonl",".errors.jsonl"))}ensureLogFileReady(){return this.initPromise||(this.initPromise=(async()=>{await g.mkdir(this.logDir,{recursive:true}),await g.writeFile(this.logPath,"","utf8");})()),this.initPromise}enqueueTask(t){this.writeQueue=this.writeQueue.then(t).catch(e=>{this.writeError=e instanceof Error?e:new Error(String(e));});}enqueueLine(t){this.enqueueTask(async()=>{await this.ensureLogFileReady(),await g.appendFile(this.logPath,`${t}
|
|
3
|
+
`,"utf8");});}enqueueJsonLine(t,e){this.enqueueTask(()=>_(t,e));}accepts(t){return L[t]<=L[this.level]}push(t,e,r){if(!this.accepts(t))return;let o=JSON.stringify({time:new Date().toISOString(),level:t,message:e,...r||{}});this.enqueueLine(o),t==="error"?console.error(`[ERROR] ${e}`,r?JSON.stringify(r):""):t==="warn"?console.warn(`[WARN] ${e}`,r?JSON.stringify(r):""):console.log(`[${t.toUpperCase().padEnd(5)}] ${e}`);}info(t,e){this.push("info",t,e);}progress(t,e){N(this.logFile,t,e);}debug(t,e){this.push("debug",t,e);}warn(t,e){this.push("warn",t,e);}error(t,e){this.enqueueJsonLine(this.errorsPath,{message:t,...e||{}}),this.push("error",t,e);}mark(t){this.marks.set(t,Date.now());}endMark(t,e){let r=this.marks.get(t);if(!r)return;let o=Date.now()-r,n=Number((o/1e3).toFixed(2));this.push("info",`Timing ${t}: ${n}s`,{name:t,ms:o,seconds:n,...e||{}});}async flush(){let t=Date.now()-this.startedAt,e=Number((t/1e3).toFixed(2));if(this.push("info",`Total time: ${e}s`,{name:"total",ms:t,seconds:e}),await this.writeQueue,this.writeError)throw this.writeError}};async function U(){let i=await y(),t=D(process.argv.slice(2)),e=C(i,t),r=e.config,o=r.logDir||"logs",n=new h(o,"invalidate.log.jsonl",r.logLevel??"info");if(e.name&&n.info(`Using deployment profile: ${e.name}`),!r.cloudFront.distributionId){n.info("No CloudFront distributionId configured; skipping invalidation."),await n.flush();return}let l=r.cloudFront.invalidationPaths.length>0?r.cloudFront.invalidationPaths:["/*"];n.info(`Starting CloudFront invalidation for distribution ${r.cloudFront.distributionId}`),n.info(`Invalidation paths (${l.length}): ${l.join(", ")}`),n.mark("invalidate");let c=new CloudFrontClient({region:"us-east-1"}),s=`smartcloud-static-publisher-${Date.now()}`;n.debug(`CallerReference: ${s}`);let a=await c.send(new CreateInvalidationCommand({DistributionId:r.cloudFront.distributionId,InvalidationBatch:{CallerReference:s,Paths:{Quantity:l.length,Items:l}}})),u=a.Invalidation?.Id??"(unknown)",p=a.Invalidation?.Status??"(unknown)";n.info(`CloudFront invalidation created. ID: ${u} Status: ${p}`),n.debug(`Full invalidation response: ${JSON.stringify(a.Invalidation)}`),n.endMark("invalidate"),await n.flush();}U().catch(async i=>{console.error(i);try{let e=(await y().catch(()=>null))?.logDir??"logs",r=new h(e,"invalidate.log.jsonl","debug");r.error(`Unhandled error: ${i instanceof Error?i.message:String(i)}`,i instanceof Error?{stack:i.stack}:void 0),await r.flush();}catch{}process.exit(1);});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import {pathToFileURL}from'url';import {PLUGIN_KEY}from'@smart-cloud/publisher-core/constants';import g from'fs/promises';import {existsSync}from'fs';import d from'path';import {spawn}from'child_process';import {randomUUID}from'crypto';var J=class extends Error{request;step;constructor(t,r){super(`Stop requested during ${r}.`),this.name="StopRequestedError",this.request=t,this.step=r;}},W="queue-mutation.lock",K=5e3,H=3e4;function z(e){let t=process.env.STATIC_PUBLISHER_RUNTIME_DIR||process.env.WPSUITE_STATIC_PUBLISHER_RUNTIME_DIR||"",r=process.env.STATIC_PUBLISHER_EXPORTER_DIR||process.cwd(),n=process.env.PUBLISHER_CONFIG||"",s=1;for(let u=0;u<e.length;u++){let o=e[u];o==="--runtime-dir"?t=e[++u]||"":o.startsWith("--runtime-dir=")?t=o.slice(14):o==="--exporter-dir"?r=e[++u]||r:o.startsWith("--exporter-dir=")?r=o.slice(15):o==="--config"?n=e[++u]||"":o.startsWith("--config=")?n=o.slice(9):o==="--max-jobs"?s=Number.parseInt(e[++u]||"1",10):o.startsWith("--max-jobs=")&&(s=Number.parseInt(o.slice(11),10));}if(!t)throw new Error("Missing runtime dir. Use --runtime-dir or STATIC_PUBLISHER_RUNTIME_DIR.");let c=d.resolve(t),i=n?d.resolve(n):d.join(c,"config.json");return {runtimeDir:c,exporterDir:d.resolve(r),configPath:i,maxJobs:Number.isFinite(s)&&s>0?s:1}}async function h(e,t){try{let r=await g.readFile(e,"utf8");return JSON.parse(r)}catch(r){if((r&&typeof r=="object"&&"code"in r?String(r.code||""):"")!=="ENOENT"){let s=r instanceof Error?r.message:String(r);console.warn(`[queue-runner] failed to read JSON ${e}: ${s}`);}return t}}async function f(e,t){await g.mkdir(d.dirname(e),{recursive:true}),await g.writeFile(e,JSON.stringify(t,null,2),"utf8");}function M(e){return d.join(e,W)}async function G(e){await new Promise(t=>setTimeout(t,e));}async function X(e){let t=M(e),r=Date.now()+K;for(;;)try{await g.mkdir(d.dirname(t),{recursive:!0}),await g.writeFile(t,JSON.stringify({pid:process.pid,createdAt:new Date().toISOString()},null,2),{encoding:"utf8",flag:"wx"});return}catch(n){if((n&&typeof n=="object"&&"code"in n?String(n.code||""):"")!=="EEXIST")throw n;let c=await g.stat(t).catch(()=>null);if(c&&Date.now()-c.mtimeMs>H){await g.unlink(t).catch(()=>{});continue}if(Date.now()>=r)throw new Error("Timed out acquiring queue mutation lock.",{cause:n});await G(50);}}async function Y(e){await g.unlink(M(e)).catch(()=>{});}async function R(e,t){await X(e);try{return await t()}finally{await Y(e);}}function Z(e){return d.join(e,"queue-runner-heartbeat.json")}function B(e){return d.join(e,"current-progress.json")}function T(e){return d.join(e,"scheduler-state.json")}function V(e,t){let n=String(e||"").replace(/\\/g,"/").trim().replace(/^\/+|\/+$/g,"");if(!n)return t;let s=n.split("/").map(c=>c.trim()).filter(c=>c.length>0&&c!=="."&&c!=="..");return s.length>0?s.join("/"):t}async function ee(e){let t=await h(e.configPath||"",null),r=d.resolve(e.runtimeDir,".."),s=(typeof t?.logDir=="string"?t.logDir:"").trim();return s&&d.isAbsolute(s)?d.resolve(s):d.resolve(r,V(s,"logs"))}function C(e,t){return String(e||"").trim().replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||t}function te(e){let t=e?new Date(e):new Date;return (Number.isNaN(t.getTime())?new Date:t).toISOString().replace(/[-:]/g,"").replace(/\.\d{3}Z$/,"Z")}function re(e){return [te(e.endedAt||e.startedAt),C(e.command||"job","job"),C(e.id||"job","job"),C(e.status||"finished","finished")].join("-")}function ne(e){return e.endsWith(".log.jsonl")||e.endsWith(".errors.jsonl")?true:["current-crawl-event.json","rejected.jsonl","ignored.jsonl","skipped-http.jsonl","errors.jsonl","timings.jsonl","rejected.json","ignored.json","skipped-http.json","errors.json","timings.json"].includes(e)}async function k(e,t){let r=await ee(t),n=new Date().toISOString(),s=d.join(r,"archive",re(e)),c=[];await g.mkdir(s,{recursive:true});try{let u=await g.readdir(r,{withFileTypes:!0});for(let o of u)!o.isFile()||!ne(o.name)||(await g.copyFile(d.join(r,o.name),d.join(s,o.name)),c.push(o.name));}catch(u){if((u&&typeof u=="object"&&"code"in u?String(u.code||""):"")!=="ENOENT")throw u}let i=B(t.runtimeDir);return existsSync(i)&&(await g.copyFile(i,d.join(s,"current-progress.json")),c.push("current-progress.json")),await f(d.join(s,"job.json"),{archivedAt:n,archiveDir:s,logDir:r,runtimeDir:t.runtimeDir,exporterDir:t.exporterDir,configPath:t.configPath||"",archivedFiles:c,job:e}),c.push("job.json"),{archiveDir:s,archiveCreatedAt:n,archivedFiles:c}}function ie(e){return d.join(e,"audit-events.jsonl")}function D(e){return d.join(e,"stop-request.json")}async function O(e){try{let t=await g.readFile(D(e),"utf8"),r=JSON.parse(t);return r&&typeof r=="object"?r:null}catch{return null}}async function j(e,t){let r=await O(e);if(!r)return null;let n=String(r.targetJobId||"").trim();return n&&n!==t?null:r}async function E(e,t){if(!t){await g.unlink(D(e)).catch(()=>{});return}let r=await O(e);if(!r)return;let n=String(r.targetJobId||"").trim();(!n||n===t)&&await g.unlink(D(e)).catch(()=>{});}async function A(e,t){try{let r={occurredAt:t.occurredAt||new Date().toISOString(),status:t.status||"info",actorSource:t.actorSource||"queue-runner",...t};await g.mkdir(e,{recursive:!0}),await g.appendFile(ie(e),`${JSON.stringify(r)}
|
|
2
|
+
`,"utf8");}catch{}}async function p(e,t,r){let n={checkedAt:new Date().toISOString(),status:t,pid:process.pid,nodePath:process.execPath,nodeVersion:process.version,runtimeDir:e.runtimeDir,exporterDir:e.exporterDir,...r??{}};try{await f(Z(e.runtimeDir),n);}catch{}}async function I(e){try{await g.unlink(B(e));}catch{}}function oe(e,t){let r=e.command;if(r!=="publish"&&r!=="crawl"&&r!=="deploy"&&r!=="invalidate"&&r!=="retry-timeouts"&&r!=="url")return null;let n=Number.parseInt(String(e.intervalMinutes??"0"),10);if(!Number.isFinite(n)||n<1)return null;let s=(e.id||`${r}-${t+1}`).trim();if(!s)return null;let c=e.enabled!==false,i=typeof e.url=="string"?e.url.trim():"",u=typeof e.deploymentProfile=="string"?e.deploymentProfile.trim():"",o=(r==="publish"||r==="crawl")&&e.crawlMode==="incremental"?"incremental":"full";return r==="url"&&!i?null:{id:s,enabled:c,command:r,intervalMinutes:n,...r==="publish"||r==="crawl"?{crawlMode:o}:{},...(r==="publish"||r==="deploy"||r==="invalidate")&&u?{deploymentProfile:u}:{},...i?{url:i}:{}}}async function se(e){let t=await h(e.configPath||"",null),r=t?.scheduler,n=Array.isArray(r?.rules)?r.rules.map((s,c)=>oe(s,c)).filter(s=>!!s):[];return {enabled:!!r?.enabled,timezone:typeof r?.timezone=="string"&&r.timezone.trim()?r.timezone.trim():"UTC",rules:n,wpsuite:t?.wpsuite}}function ue(e,t,r){return e.some(n=>n.command===t&&(n.url||"").trim()===(r||"").trim()&&(n.status===void 0||n.status==="queued"||n.status==="running"))}async function ae(e,t){if(!t.allowSchedulerAutoEnqueue)return 0;let r=await se(e);if(!r.enabled||r.rules.length===0)return 0;if(t.assertActiveSubscriptionForIdentity)try{await t.assertActiveSubscriptionForIdentity(r.wpsuite??null);}catch(o){let a=o instanceof Error?o.message:String(o);return console.warn(`[queue-runner] scheduler auto-enqueue skipped: ${a}`),0}let n=d.join(e.runtimeDir,"queue.json"),s=d.join(e.runtimeDir,"current-run.json"),c=Date.now(),i=0,u=[];await R(e.runtimeDir,async()=>{let o=await h(n,[]),a=await h(s,null),w=await h(T(e.runtimeDir),{lastEnqueuedBucketByRuleId:{}}),m=[...o];a&&a.status==="running"&&m.unshift(a);for(let l of r.rules){if(!l.enabled)continue;let P=l.intervalMinutes*60*1e3,S=Math.floor(c/P),v=w.lastEnqueuedBucketByRuleId[l.id]??-1;if(S<=v||ue(m,l.command,l.url))continue;let q={id:randomUUID(),command:l.command,...(l.command==="publish"||l.command==="crawl")&&l.crawlMode?{crawlMode:l.crawlMode}:{},...(l.command==="publish"||l.command==="deploy"||l.command==="invalidate")&&l.deploymentProfile?{deploymentProfile:l.deploymentProfile}:{},...l.url?{url:l.url}:{},enqueueSource:"scheduler",...r.wpsuite?{wpsuite:r.wpsuite}:{},status:"queued",createdAt:new Date().toISOString(),createdBy:0};o.push(q),m.push(q),u.push({job:q,rule:l}),w.lastEnqueuedBucketByRuleId[l.id]=S,i+=1;}i>0&&(await f(n,o),await f(T(e.runtimeDir),w));});for(let o of u)await A(e.runtimeDir,{eventType:"job-created",status:"queued",actorSource:"queue-runner-scheduler",jobId:o.job.id,command:o.job.command,message:"Scheduler auto-enqueued a job.",details:{ruleId:o.rule.id,intervalMinutes:o.rule.intervalMinutes,timezone:r.timezone,deploymentProfile:o.rule.deploymentProfile||"",url:o.rule.url||""}});return i}async function ce(e){let t={pid:process.pid,startedAt:new Date().toISOString()};await g.writeFile(e,JSON.stringify(t,null,2),{encoding:"utf8",flag:"wx"});}async function de(e){try{await g.unlink(e);}catch{}}function le(e,t){let r=[d.join(e,"dist",`${t}.js`),d.join(e,`${t}.js`)];for(let n of r)if(existsSync(n))return n;throw new Error(`Cannot find ${t}.js in ${d.join(e,"dist")} or ${e}`)}async function y(e,t,r,n,s,c,i){if(i){let a=await j(t,i);if(a)throw new J(a,r)}let u=le(e,r),o={...process.env,...c??{}};return o.STATIC_PUBLISHER_RUNTIME_DIR=t,s&&(o.PUBLISHER_CONFIG=s),await new Promise((a,w)=>{let m=null,l=null,P=null,S=false,v=spawn(process.execPath,[u,...n],{cwd:e,env:o,stdio:"inherit"}),q=()=>{l&&clearInterval(l),P&&clearTimeout(P);},U=async()=>{if(!(!i||m||S)){S=true;try{let b=await j(t,i);if(!b)return;m=b,v.kill("SIGTERM"),P=setTimeout(()=>{v.kill("SIGKILL");},1e4);}finally{S=false;}}};i&&(l=setInterval(()=>{U();},1e3)),v.on("error",b=>{q(),w(b);}),v.on("close",(b,x)=>{if(q(),m){w(new J(m,r));return}b===0?a():w(new Error(`${r} exited with code ${b??-1}${x?` (signal ${x})`:""}`));});}),0}function me(e){let t=e.awsTempCreds;if(!t)return {};let r={};return typeof t.accessKeyId=="string"&&t.accessKeyId.trim()!==""&&(r.AWS_ACCESS_KEY_ID=t.accessKeyId.trim()),typeof t.secretAccessKey=="string"&&t.secretAccessKey.trim()!==""&&(r.AWS_SECRET_ACCESS_KEY=t.secretAccessKey.trim()),typeof t.sessionToken=="string"&&t.sessionToken.trim()!==""&&(r.AWS_SESSION_TOKEN=t.sessionToken.trim()),r}async function ge(e,t){let r=e.wpsuite;if(r&&(r.accountId||r.siteId||r.siteKey))return r;let s=(await h(t.configPath||"",null))?.wpsuite;return s&&(s.accountId||s.siteId||s.siteKey)?s:null}async function pe(e,t){return await ge(e,t)}function F(e){let t={...e,status:"queued"};return delete t.startedAt,delete t.endedAt,delete t.exitCode,delete t.error,t}async function fe(e){let t=d.join(e.runtimeDir,"queue.json"),r=d.join(e.runtimeDir,"current-run.json"),n=await R(e.runtimeDir,async()=>{let s=await h(r,null);if(!s||s.status!=="running"&&s.status!=="queued")return null;let c=await h(t,[]),i=Array.isArray(c)?c.filter(o=>o?.id!==s.id):[],u=F(s);return await f(t,[u,...i]),await f(r,null),u});n&&(await E(e.runtimeDir,n.id),await A(e.runtimeDir,{eventType:"job-recovered",status:"queued",actorSource:"queue-runner",jobId:n.id,command:n.command,message:"Recovered stale current-run entry back into queue."}));}async function he(e,t,r){try{if((r.shouldEnforceSubscriptionOnJobExecution?.(e)??r.enforceSubscriptionOnJobExecution)&&r.assertActiveSubscriptionForIdentity){let u=await pe(e,t);await r.assertActiveSubscriptionForIdentity(u);}let s=me(e),c=e.crawlMode==="incremental"?["--crawl-mode","incremental"]:[],i=e.deploymentProfile?["--profile",e.deploymentProfile]:[];if(e.command==="publish")return await p(t,"running",{currentJobId:e.id,currentJobCommand:e.command,currentStep:"crawl"}),await y(t.exporterDir,t.runtimeDir,"crawl",c,t.configPath,s,e.id),await p(t,"running",{currentJobId:e.id,currentJobCommand:e.command,currentStep:"deploy"}),await y(t.exporterDir,t.runtimeDir,"deploy",i,t.configPath,s,e.id),await p(t,"running",{currentJobId:e.id,currentJobCommand:e.command,currentStep:"invalidate"}),await y(t.exporterDir,t.runtimeDir,"invalidate",i,t.configPath,s,e.id),{exitCode:0};if(e.command==="crawl")return await p(t,"running",{currentJobId:e.id,currentJobCommand:e.command,currentStep:"crawl"}),await y(t.exporterDir,t.runtimeDir,"crawl",c,t.configPath,s,e.id),{exitCode:0};if(e.command==="deploy")return await p(t,"running",{currentJobId:e.id,currentJobCommand:e.command,currentStep:"deploy"}),await y(t.exporterDir,t.runtimeDir,"deploy",i,t.configPath,s,e.id),{exitCode:0};if(e.command==="invalidate")return await p(t,"running",{currentJobId:e.id,currentJobCommand:e.command,currentStep:"invalidate"}),await y(t.exporterDir,t.runtimeDir,"invalidate",i,t.configPath,s,e.id),{exitCode:0};if(e.command==="retry-timeouts")return await p(t,"running",{currentJobId:e.id,currentJobCommand:e.command,currentStep:"retry-timeouts"}),await y(t.exporterDir,t.runtimeDir,"crawl",["--retry-timeouts"],t.configPath,s,e.id),{exitCode:0};if(e.command==="url"){let u=(e.url||"").trim();return u?(await p(t,"running",{currentJobId:e.id,currentJobCommand:e.command,currentStep:"url"}),await y(t.exporterDir,t.runtimeDir,"crawl",["--url",u],t.configPath,s,e.id),{exitCode:0}):{exitCode:2,error:"Missing url for command 'url'"}}return {exitCode:2,error:`Unsupported command: ${e.command}`}}catch(n){return n instanceof J?{exitCode:130,error:"Job stop requested.",stopped:true,stopRequest:n.request,stoppedStep:n.step}:{exitCode:1,error:n instanceof Error?n.message:String(n)}}}function L(e){return async function(r=process.argv.slice(2)){let n=z(r),s=d.join(n.runtimeDir,"export.lock"),c=d.join(n.runtimeDir,"last-run.json");await I(n.runtimeDir),await p(n,"starting",{message:"queue-runner starting"});try{await ce(s);}catch(i){if((i&&typeof i=="object"&&"code"in i?String(i.code||""):"")!=="EEXIST")throw i;console.log("[queue-runner] lock active, skipping"),await p(n,"lock-active",{message:"lock active, skipped this cron tick"});return}try{await fe(n);let i=await ae(n,e),u=0;for(let o=0;o<n.maxJobs;o++){let a=await ye(n,e);if(a==="none"||(u+=1,a==="stopped"))break}if(u===0)await I(n.runtimeDir),await p(n,"idle",{processedJobs:u,schedulerEnqueued:i,message:"no queued jobs"});else {await I(n.runtimeDir);let o=await h(c,null),a=String(o?.status||"").trim();await p(n,a==="success"?"job-success":a==="stopped"?"job-stopped":"job-failed",{processedJobs:u,schedulerEnqueued:i,lastJobId:o?.id,lastJobCommand:o?.command,lastJobStatus:o?.status,lastJobExitCode:o?.exitCode,lastJobError:o?.error,...a==="stopped"?{currentStep:o?.stoppedStep,stopRequestedAt:o?.stopRequestedAt,stopRequestedByLogin:o?.stopRequestedByLogin,stopRequestedMode:o?.stopMode,lastStoppedStep:o?.stoppedStep,message:`Job stopped during ${o?.stoppedStep||o?.command||"active step"} and requeued.`}:{}});}}catch(i){let u=i instanceof Error?i.message:String(i);throw await p(n,"error",{message:u}),await A(n.runtimeDir,{eventType:"queue-runner-error",status:"failed",actorSource:"queue-runner",message:"Unhandled queue-runner error.",details:{error:u}}),i}finally{await de(s);}}}async function we(e,t){let r=d.join(e.runtimeDir,"queue.json"),n=d.join(e.runtimeDir,"current-run.json");await R(e.runtimeDir,async()=>{let s=await h(r,[]),c=Array.isArray(s)?s.filter(i=>i?.id!==t.id):[];await f(r,[F(t),...c]),await f(n,null);});}async function ye(e,t){let r=d.join(e.runtimeDir,"queue.json"),n=d.join(e.runtimeDir,"current-run.json"),s=d.join(e.runtimeDir,"last-run.json"),c=new Date().toISOString(),i=await R(e.runtimeDir,async()=>{let m=await h(r,[]);if(!Array.isArray(m)||m.length===0)return null;let l={...m[0],status:"running",startedAt:c};return await f(r,m.slice(1)),await f(n,l),l});if(!i)return "none";await I(e.runtimeDir),await A(e.runtimeDir,{eventType:"job-run-started",status:"running",actorSource:"queue-runner",jobId:i.id,command:i.command,message:"Job execution started.",details:{createdAt:i.createdAt||"",startedAt:c,createdBy:i.createdBy??null,deploymentProfile:i.deploymentProfile||"",queuedWithTempAwsCreds:!!i.awsTempCreds}}),await p(e,"running",{currentJobId:i.id,currentJobCommand:i.command,currentStep:i.command});let u=await he(i,e,t),o=new Date().toISOString();if(u.stopped){let m={...i,status:"stopped",endedAt:o,exitCode:u.exitCode,...u.error?{error:u.error}:{},stopRequestedAt:u.stopRequest?.requestedAt||"",stopRequestedByUserId:typeof u.stopRequest?.requestedByUserId=="number"?u.stopRequest.requestedByUserId:null,stopRequestedByLogin:u.stopRequest?.requestedByLogin||"",stopMode:u.stopRequest?.mode||"requeue",stoppedStep:u.stoppedStep||i.command};await we(e,i);try{let l=await k(m,e);m.logArchiveDir=l.archiveDir,m.logArchiveCreatedAt=l.archiveCreatedAt,m.logArchiveFileCount=l.archivedFiles.length;}catch(l){m.logArchiveError=l instanceof Error?l.message:String(l);}return await f(s,m),await E(e.runtimeDir,i.id),await I(e.runtimeDir),await A(e.runtimeDir,{eventType:"job-run-stopped",status:"stopped",actorSource:"queue-runner",jobId:i.id,command:i.command,message:"Job stop requested and requeued.",details:{startedAt:i.startedAt||"",endedAt:o,deploymentProfile:i.deploymentProfile||"",stopMode:u.stopRequest?.mode||"requeue",stopRequestedAt:u.stopRequest?.requestedAt||"",stopRequestedByLogin:u.stopRequest?.requestedByLogin||"",stopRequestedByUserId:typeof u.stopRequest?.requestedByUserId=="number"?u.stopRequest.requestedByUserId:null,stoppedStep:u.stoppedStep||i.command,logArchiveDir:m.logArchiveDir||"",logArchiveCreatedAt:m.logArchiveCreatedAt||"",logArchiveFileCount:m.logArchiveFileCount??0,logArchiveError:m.logArchiveError||""}}),"stopped"}let a={...i,status:u.exitCode===0?"success":"failed",endedAt:o,exitCode:u.exitCode,...u.error?{error:u.error}:{}};await f(n,null),await E(e.runtimeDir,i.id);try{let m=await k(a,e);a.logArchiveDir=m.archiveDir,a.logArchiveCreatedAt=m.archiveCreatedAt,a.logArchiveFileCount=m.archivedFiles.length;}catch(m){a.logArchiveError=m instanceof Error?m.message:String(m);}await f(s,a);let w=i.startedAt&&a.endedAt?Math.max(0,Math.round((new Date(a.endedAt).getTime()-new Date(i.startedAt).getTime())/1e3)):void 0;return await A(e.runtimeDir,{eventType:"job-run-finished",status:a.status==="success"?"success":"failed",actorSource:"queue-runner",jobId:a.id,command:a.command,message:a.status==="success"?"Job execution finished successfully.":"Job execution finished with failure.",details:{startedAt:i.startedAt||"",endedAt:a.endedAt||"",durationSec:w,deploymentProfile:a.deploymentProfile||"",exitCode:a.exitCode??null,error:a.error||"",logArchiveDir:a.logArchiveDir||"",logArchiveCreatedAt:a.logArchiveCreatedAt||"",logArchiveFileCount:a.logArchiveFileCount??0,logArchiveError:a.logArchiveError||""}}),"processed"}function ve(e){let t=(e.apiBase||"").trim();if(t)return t.replace(/\/$/,"");let r=(process.env.WPSUITE_API_BASE_URL||"").trim();return r?r.replace(/\/$/,""):"https://api.wpsuite.io"}async function qe(e){if(!e)throw new Error("Missing WPSuite identity (accountId/siteId/siteKey). Link the site before running business logic.");let t=String(e.accountId||"").trim(),r=String(e.siteId||"").trim(),n=String(e.siteKey||"").trim();if(!t||!r||!n)throw new Error("Missing WPSuite identity (accountId/siteId/siteKey). Link the site before running business logic.");let c=`${ve(e)}/account/${encodeURIComponent(t)}/site/${encodeURIComponent(r)}/license`,i=new AbortController,u=setTimeout(()=>i.abort(),12e3);try{let o=await fetch(c,{method:"GET",headers:{Accept:"application/json","X-Site-Key":n,"X-Plugin":PLUGIN_KEY},signal:i.signal});if(!o.ok)throw new Error(`Subscription check failed with HTTP ${o.status}`);let a=await o.json().catch(()=>null);if(!a||!a.config||!a.jws)throw new Error("No active subscription payload received from wpsuite API.")}finally{clearTimeout(u);}}var Ae={allowSchedulerAutoEnqueue:true,enforceSubscriptionOnJobExecution:false,shouldEnforceSubscriptionOnJobExecution:e=>e.enqueueSource==="scheduler"||e.createdBy===0||e.crawlMode==="incremental",assertActiveSubscriptionForIdentity:qe},N=L(Ae),Pe=N,je=N;process.argv[1]&&import.meta.url===pathToFileURL(process.argv[1]).href&&Pe().catch(e=>{console.error(e),process.exit(1);});
|
|
3
|
+
export{je as default,Pe as main,N as runQueueRunner};
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@smart-cloud/publisher-exporter",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
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.",
|
|
7
|
+
"bin": {
|
|
8
|
+
"publisher-exporter": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"scripts/queue-runner-cron.sh",
|
|
13
|
+
"publisher.config.example.json",
|
|
14
|
+
"README.md",
|
|
15
|
+
"package.json"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=20"
|
|
19
|
+
},
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"registry": "https://registry.npmjs.org/",
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"prune": "npm ci --omit=dev",
|
|
26
|
+
"build": "tsup --config tsup.config.ts",
|
|
27
|
+
"build:runtime-zip": "node ./scripts/build-runtime-zip.mjs",
|
|
28
|
+
"check": "tsc -p tsconfig.json --noEmit",
|
|
29
|
+
"lint": "eslint 'src/**/*.ts' --report-unused-disable-directives --max-warnings 0",
|
|
30
|
+
"crawl": "node dist/crawl.js",
|
|
31
|
+
"crawl:retry-timeouts": "node dist/crawl.js --retry-timeouts",
|
|
32
|
+
"crawl:url": "node dist/crawl.js --url",
|
|
33
|
+
"crawl:urls": "node dist/crawl.js --urls",
|
|
34
|
+
"deploy": "node dist/deploy.js",
|
|
35
|
+
"invalidate": "node dist/invalidate.js",
|
|
36
|
+
"queue:run": "node dist/queue-runner.js",
|
|
37
|
+
"queue:drain": "node dist/queue-runner.js --max-jobs=100",
|
|
38
|
+
"queue:cron": "bash ./scripts/queue-runner-cron.sh",
|
|
39
|
+
"publish:site": "npm run build && npm run crawl && npm run deploy && npm run invalidate",
|
|
40
|
+
"install:browsers": "playwright install chromium",
|
|
41
|
+
"release:npm": "WPSUITE_PREMIUM=true npm run build && npm publish --access=public"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@smart-cloud/publisher-core": "^1.0.2",
|
|
45
|
+
"@aws-sdk/client-cloudfront": "^3.1043.0",
|
|
46
|
+
"@aws-sdk/client-s3": "^3.1043.0",
|
|
47
|
+
"@aws-sdk/lib-storage": "^3.1043.0",
|
|
48
|
+
"fast-glob": "^3.3.3",
|
|
49
|
+
"mime-types": "^3.0.2",
|
|
50
|
+
"playwright": "^1.59.1"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@eslint/js": "^10.0.1",
|
|
54
|
+
"@types/mime-types": "^3.0.1",
|
|
55
|
+
"@types/node": "^25.6.0",
|
|
56
|
+
"@typescript-eslint/eslint-plugin": "^8.59.2",
|
|
57
|
+
"@typescript-eslint/parser": "^8.59.2",
|
|
58
|
+
"esbuild": "^0.28.0",
|
|
59
|
+
"eslint": "^10.3.0",
|
|
60
|
+
"globals": "^17.6.0",
|
|
61
|
+
"tsup": "^8.5.1",
|
|
62
|
+
"typescript": "^6.0.3",
|
|
63
|
+
"typescript-eslint": "^8.59.2"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sourceOrigin": "http://wpsuite.local",
|
|
3
|
+
"targetOrigin": "https://wpsuite.io",
|
|
4
|
+
"outputDir": "export",
|
|
5
|
+
"seedPaths": ["/", "/gatey/", "/ai-kit/", "/flow/", "/blog/", "/docs/"],
|
|
6
|
+
"allowedAssetHosts": ["wpsuite.local", "localhost", "dev.wpsuite.io"],
|
|
7
|
+
"blockedPathPrefixes": [
|
|
8
|
+
"/wp-admin",
|
|
9
|
+
"/wp-login.php",
|
|
10
|
+
"/wp-json",
|
|
11
|
+
"/xmlrpc.php",
|
|
12
|
+
"/wp-cron.php"
|
|
13
|
+
],
|
|
14
|
+
"blockedSearchFragments": [
|
|
15
|
+
"preview=true",
|
|
16
|
+
"elementor-preview",
|
|
17
|
+
"customize_changeset_uuid"
|
|
18
|
+
],
|
|
19
|
+
"extraReplacements": {
|
|
20
|
+
"https://dev.wpsuite.io": "https://wpsuite.io",
|
|
21
|
+
"http://localhost:8080": "https://wpsuite.io"
|
|
22
|
+
},
|
|
23
|
+
"readiness": {
|
|
24
|
+
"waitForSelector": null,
|
|
25
|
+
"waitForFunction": "window.__WPSUITE_STATIC_READY__ === true",
|
|
26
|
+
"timeoutMs": 1500,
|
|
27
|
+
"fallbackWaitMs": 1500
|
|
28
|
+
},
|
|
29
|
+
"viewport": {
|
|
30
|
+
"width": 1440,
|
|
31
|
+
"height": 1200
|
|
32
|
+
},
|
|
33
|
+
"maxPages": 0,
|
|
34
|
+
"concurrency": 2,
|
|
35
|
+
"assetDownloadConcurrency": 6,
|
|
36
|
+
"logLevel": "info",
|
|
37
|
+
"s3SyncMode": "sdk-upload-delete",
|
|
38
|
+
"s3": {
|
|
39
|
+
"bucket": "wpsuite-static-prod",
|
|
40
|
+
"prefix": "",
|
|
41
|
+
"region": "eu-central-1",
|
|
42
|
+
"htmlCacheControl": "public,max-age=0,must-revalidate",
|
|
43
|
+
"assetCacheControl": "public,max-age=31536000,immutable"
|
|
44
|
+
},
|
|
45
|
+
"cloudFront": {
|
|
46
|
+
"distributionId": "E1234567890",
|
|
47
|
+
"invalidationPaths": ["/*"]
|
|
48
|
+
},
|
|
49
|
+
"urlRewriteMode": "absolute",
|
|
50
|
+
"navigationTimeoutMs": 30000
|
|
51
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
RUNTIME_DIR="${STATIC_PUBLISHER_RUNTIME_DIR:-${1:-}}"
|
|
5
|
+
MAX_JOBS="${STATIC_PUBLISHER_MAX_JOBS:-${2:-1}}"
|
|
6
|
+
PACKAGE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
7
|
+
CONFIG_PATH="${PUBLISHER_CONFIG:-$RUNTIME_DIR/config.json}"
|
|
8
|
+
|
|
9
|
+
if [[ -z "$RUNTIME_DIR" ]]; then
|
|
10
|
+
echo "Missing runtime dir. Provide arg1 or STATIC_PUBLISHER_RUNTIME_DIR." >&2
|
|
11
|
+
exit 2
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
resolve_node_bin() {
|
|
15
|
+
if [[ -n "${NODE_BIN:-}" && -x "${NODE_BIN}" ]]; then
|
|
16
|
+
printf '%s\n' "$NODE_BIN"
|
|
17
|
+
return 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
if command -v node >/dev/null 2>&1; then
|
|
21
|
+
command -v node
|
|
22
|
+
return 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
if [[ -n "${NVM_DIR:-}" && -s "${NVM_DIR}/nvm.sh" ]]; then
|
|
26
|
+
# shellcheck source=/dev/null
|
|
27
|
+
. "${NVM_DIR}/nvm.sh"
|
|
28
|
+
elif [[ -s "$HOME/.nvm/nvm.sh" ]]; then
|
|
29
|
+
# shellcheck source=/dev/null
|
|
30
|
+
. "$HOME/.nvm/nvm.sh"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
if command -v node >/dev/null 2>&1; then
|
|
34
|
+
command -v node
|
|
35
|
+
return 0
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
local candidate
|
|
39
|
+
for candidate in \
|
|
40
|
+
"/usr/bin/node" \
|
|
41
|
+
"/usr/local/bin/node" \
|
|
42
|
+
"$HOME/.nvm/versions/node/current/bin/node"; do
|
|
43
|
+
if [[ -x "$candidate" ]]; then
|
|
44
|
+
printf '%s\n' "$candidate"
|
|
45
|
+
return 0
|
|
46
|
+
fi
|
|
47
|
+
done
|
|
48
|
+
|
|
49
|
+
return 1
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
NODE_CMD="$(resolve_node_bin || true)"
|
|
53
|
+
if [[ -z "$NODE_CMD" ]]; then
|
|
54
|
+
echo "node binary not found in cron environment. Set NODE_BIN or NVM_DIR in crontab." >&2
|
|
55
|
+
echo "PATH=$PATH" >&2
|
|
56
|
+
exit 127
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
exec "$NODE_CMD" "$PACKAGE_DIR/dist/cli.js" queue-runner \
|
|
60
|
+
--runtime-dir "$RUNTIME_DIR" \
|
|
61
|
+
--exporter-dir "$PACKAGE_DIR" \
|
|
62
|
+
--config "$CONFIG_PATH" \
|
|
63
|
+
--max-jobs "$MAX_JOBS"
|