@smart-cloud/publisher-exporter 1.1.60 → 1.1.62

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 CHANGED
@@ -171,6 +171,85 @@ Archived files are gzip-compressed per file and listed in `job.json`. WordPress
171
171
 
172
172
  Prune old archive directories with `publisher-exporter prune-logs --runtime-dir /srv/site/runtime --older-than-days 30`. Add that command to daily cron or another retention scheduler that matches your log policy.
173
173
 
174
+ ## Optional Lambda workers
175
+
176
+ The exporter includes protocol-v1 building blocks for delegating bounded
177
+ render, text-rewrite, and S3 deploy-copy tasks to container-image Lambda
178
+ functions. Large payloads do not travel in the Lambda response: workers write
179
+ HTML and detailed results below a configured S3 workspace prefix and return a
180
+ small `resultKey` pointer. Reusing the same job/task ID is idempotent.
181
+
182
+ The companion CDK application is maintained separately at
183
+ [`smartcloudsol/static-publisher-lambda-workers`](https://github.com/smartcloudsol/static-publisher-lambda-workers).
184
+ directory. Its deploy script emits a `remote-workers.json` containing the
185
+ versioned Lambda alias ARNs, workspace bucket/prefix, region, and generated
186
+ assumable role. The file contains no static AWS access keys.
187
+
188
+ Install that output with owner-only permissions at
189
+ `<site-runtime>/remote-workers.json`, then validate the role, aliases, and
190
+ function runtimes:
191
+
192
+ ```bash
193
+ publisher-exporter remote-worker-health \
194
+ --runtime-dir /srv/site/runtime
195
+ ```
196
+
197
+ Protocol v1 accepts 1-20 same-origin URLs per render task. Start with one URL
198
+ per invocation so retries stay small and Lambda concurrency supplies the
199
+ parallelism. The Lambda response is not the rendered body; read the returned
200
+ S3 result object using the same temporary role credentials.
201
+
202
+ Enable the desired Lambda phases in the Static Publisher WordPress admin. The saved
203
+ runtime configuration contains only the operational settings:
204
+
205
+ ```json
206
+ {
207
+ "remoteWorkers": {
208
+ "render": {
209
+ "enabled": true,
210
+ "concurrency": 4,
211
+ "maxAttempts": 2
212
+ },
213
+ "rewrite": {
214
+ "enabled": true,
215
+ "concurrency": 8,
216
+ "batchSize": 25,
217
+ "maxAttempts": 2
218
+ },
219
+ "deploy": {
220
+ "enabled": true,
221
+ "concurrency": 8,
222
+ "batchSize": 50,
223
+ "maxAttempts": 2
224
+ }
225
+ }
226
+ }
227
+ ```
228
+
229
+ The normal queue runner already supplies the runtime directory, so cron needs
230
+ no Lambda-specific environment variables. A one-off diagnostic crawl can still
231
+ override the generated file explicitly:
232
+
233
+ ```bash
234
+ STATIC_PUBLISHER_RUNTIME_DIR=/srv/site/runtime \
235
+ publisher-exporter crawl --remote-render
236
+ ```
237
+
238
+ Use `--local-render` to override an enabled configuration for a diagnostic run.
239
+ The crawl coordinator keeps sitemap discovery, incremental decisions, asset
240
+ downloads, manifests, and deploy planning local. Each admitted
241
+ page is rendered by one Lambda task, and its HTML is read from S3 with length
242
+ and SHA-256 verification. Failed/deferred tasks receive a new task ID when
243
+ retried. Final text files are staged in the workspace, rewritten in bounded
244
+ Lambda batches, checksum-verified, and restored to the local export tree.
245
+ Deployment stages finalized objects with their cache and checksum metadata;
246
+ deploy workers then check or copy them directly from S3 to the allowlisted
247
+ target. Only explicit deploy-plan tombstones may be deleted. Each completed
248
+ batch updates phase-scoped runtime progress and adds a structured batch summary
249
+ to `deploy.log.jsonl`; debug logging also records each returned object result.
250
+ There is no silent local fallback for any
251
+ enabled remote phase.
252
+
174
253
  ## Troubleshooting
175
254
 
176
255
  - If exported HTML still contains old rewritten URLs after a rewrite fix, run `crawl --resume-rewrite`. This reprocesses every text file in the current output tree and does not depend on earlier `rewriteComplete: true` entries in `crawl-manifest.json`.
package/dist/cli.js CHANGED
@@ -1,23 +1,31 @@
1
1
  #!/usr/bin/env node
2
- import {spawn}from'child_process';import {existsSync,readFileSync}from'fs';import o from'path';import {fileURLToPath}from'url';var p=o.dirname(fileURLToPath(import.meta.url)),l=o.resolve(p,"..");function f(){try{let n=readFileSync(o.join(l,"package.json"),"utf8"),e=JSON.parse(n);if(typeof e.version=="string"&&e.version.trim())return e.version.trim()}catch{}return "unknown"}function g(){console.log(`publisher-exporter <command> [args]
2
+ import {spawn}from'child_process';import {existsSync,readFileSync}from'fs';import o from'path';import {fileURLToPath}from'url';var p=o.dirname(fileURLToPath(import.meta.url)),l=o.resolve(p,"..");function f(){try{let e=readFileSync(o.join(l,"package.json"),"utf8"),r=JSON.parse(e);if(typeof r.version=="string"&&r.version.trim())return r.version.trim()}catch{}return "unknown"}function g(){console.log(`publisher-exporter <command> [args]
3
3
 
4
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
- content-sync Run an internal claimed content-sync job
10
- queue-runner Process queued jobs from runtime JSON
11
- prune-logs Delete archived log directories older than a retention window
12
- install-browsers Install Playwright Chromium into the current/shared browsers path
13
- version Show the installed CLI version
14
- help Show this help
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
+ content-sync Run an internal claimed content-sync job
10
+ queue-runner Process queued jobs from runtime JSON
11
+ prune-logs Delete archived log directories older than a retention window
12
+ remote-worker-health Invoke health checks on all configured Lambda workers
13
+ install-browsers Install Playwright Chromium into the current/shared browsers path
14
+ version Show the installed CLI version
15
+ help Show this help
16
+
17
+ Crawl worker options:
18
+ --remote-render Delegate Playwright page rendering to Lambda
19
+ --local-render Force local Playwright rendering
20
+ --remote-worker-config <path> Diagnostic override for runtime/remote-workers.json
15
21
 
16
22
  Examples:
17
23
  publisher-exporter crawl --crawl-mode incremental
24
+ publisher-exporter crawl --remote-render
18
25
  publisher-exporter deploy --profile prod
19
26
  publisher-exporter queue-runner --runtime-dir /srv/site/runtime --max-jobs 1
20
27
  publisher-exporter prune-logs --runtime-dir /srv/site/runtime --older-than-days 30
28
+ publisher-exporter remote-worker-health --runtime-dir /srv/site/runtime
21
29
  publisher-exporter install-browsers
22
30
  publisher-exporter --version
23
- `);}function v(){console.log(f());}function a(n){let e=o.join(p,`${n}.js`);if(!existsSync(e))throw new Error(`Missing CLI entrypoint: ${e}`);return e}function y(){let n=[o.join(l,"node_modules","playwright","cli.js"),o.join(l,"node_modules","playwright-core","cli.js")];for(let e of n)if(existsSync(e))return e;throw new Error("Playwright CLI was not found under this package. Run `npm install` first.")}function i(n,e){return new Promise((r,s)=>{let t=spawn(process.execPath,[n,...e],{cwd:l,env:process.env,stdio:"inherit"});t.on("error",s),t.on("close",(d,c)=>{if(c){s(new Error(`Command terminated by signal ${c}.`));return}r(d??1);});})}async function b(n){let e=`direct-publish-${Date.now()}`,r=await i(a("content-sync"),["--capture-baseline-cutoffs",e]);if(r!==0)return r;for(let s of ["crawl","deploy","invalidate"]){let t=await i(a(s),n);if(t!==0)return t}return i(a("content-sync"),["--establish-baselines",e])}async function x(){let[n="help",...e]=process.argv.slice(2);if(["version","--version","-v"].includes(n)){v();return}if(["help","--help","-h"].includes(n)){g();return}if(n==="publish"){process.exitCode=await b(e);return}if(n==="install-browsers"){let r=e.length>0?e:["install","chromium"];process.exitCode=await i(y(),r);return}if(n==="crawl"||n==="deploy"||n==="invalidate"||n==="queue-runner"||n==="prune-logs"||n==="content-sync"){process.exitCode=await i(a(n),e);return}throw new Error(`Unknown command: ${n}`)}x().catch(n=>{console.error(n instanceof Error?n.message:String(n)),process.exit(1);});
31
+ `);}function v(){console.log(f());}function a(e){let r=o.join(p,`${e}.js`);if(!existsSync(r))throw new Error(`Missing CLI entrypoint: ${r}`);return r}function y(){let e=[o.join(l,"node_modules","playwright","cli.js"),o.join(l,"node_modules","playwright-core","cli.js")];for(let r of e)if(existsSync(r))return r;throw new Error("Playwright CLI was not found under this package. Run `npm install` first.")}function i(e,r){return new Promise((n,s)=>{let t=spawn(process.execPath,[e,...r],{cwd:l,env:process.env,stdio:"inherit"});t.on("error",s),t.on("close",(d,c)=>{if(c){s(new Error(`Command terminated by signal ${c}.`));return}n(d??1);});})}async function b(e){let r=`direct-publish-${Date.now()}`,n=await i(a("content-sync"),["--capture-baseline-cutoffs",r]);if(n!==0)return n;for(let s of ["crawl","deploy","invalidate"]){let t=await i(a(s),e);if(t!==0)return t}return i(a("content-sync"),["--establish-baselines",r])}async function x(){let[e="help",...r]=process.argv.slice(2);if(["version","--version","-v"].includes(e)){v();return}if(["help","--help","-h"].includes(e)){g();return}if(e==="publish"){process.exitCode=await b(r);return}if(e==="install-browsers"){let n=r.length>0?r:["install","chromium"];process.exitCode=await i(y(),n);return}if(e==="crawl"||e==="deploy"||e==="invalidate"||e==="queue-runner"||e==="prune-logs"||e==="remote-worker-health"||e==="content-sync"){process.exitCode=await i(a(e),r);return}throw new Error(`Unknown command: ${e}`)}x().catch(e=>{console.error(e instanceof Error?e.message:String(e)),process.exit(1);});