datocms-plugin-asset-optimization 0.7.7 → 0.7.9

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
@@ -14,6 +14,13 @@ The DatoCMS Asset Optimization Plugin leverages Imgix's powerful image processin
14
14
  - Convert images to modern formats like AVIF or WebP
15
15
  - Track optimization progress with detailed logs
16
16
  - View statistics on storage savings
17
+ - Preview the impact of your settings before replacing anything
18
+
19
+ ## Where it lives in DatoCMS
20
+
21
+ After installation, the plugin adds an **Asset Management → Optimize assets** entry in your environment's **Configuration** section. The plugin's configuration screen also provides a shortcut button to that page.
22
+
23
+ The plugin requires the `currentUserAccessToken` permission so it can replace assets on your behalf.
17
24
 
18
25
  ## Installation
19
26
 
@@ -21,7 +28,6 @@ The DatoCMS Asset Optimization Plugin leverages Imgix's powerful image processin
21
28
  2. Go to Settings > Plugins
22
29
  3. Search for "Asset Optimization"
23
30
  4. Click "Install"
24
- 5. Configure the plugin settings as desired
25
31
 
26
32
  ## Important: Use Sandbox Environments First
27
33
 
@@ -39,11 +45,40 @@ This approach allows you to safely experiment with different optimization parame
39
45
 
40
46
  ## Usage
41
47
 
42
- 1. After installation, navigate to the plugin in your DatoCMS dashboard
48
+ 1. Open **Configuration → Optimize assets**.
43
49
  2. Configure the optimization settings according to your needs
44
- 3. Click "Start Optimization" to begin the process
45
- 4. Watch the progress as the plugin processes your assets
46
- 5. View the results including statistics on size savings
50
+ 3. Click **Preview Optimization** to dry-run the process and see expected savings without touching any assets
51
+ 4. When you're happy with the projected results, click **Start Optimization**. You'll be asked to confirm twice before any asset is replaced
52
+ 5. Watch the progress and the live activity log as the plugin processes your assets
53
+ 6. Review the final statistics, including per-category lists of optimized, skipped, and failed assets
54
+
55
+ ### Settings reference
56
+
57
+ The form groups settings into four sections:
58
+
59
+ - **Size Thresholds**
60
+ - *Large Asset (MB)* and *Very Large Asset (MB)* — assets above each threshold get their own quality and resize profile
61
+ - *Minimum Size Reduction (%)* — only replace an asset if Imgix can shrink it by at least this much
62
+
63
+ - **Basic Optimization**
64
+ - *Preserve Original Format* — keep JPG/PNG/etc. instead of converting (`fm` parameter)
65
+ - *Auto Compress* — let Imgix pick compression automatically (`auto=compress`)
66
+ - *Target Format* (when not preserving) — `webp` or `avif`
67
+ - *Resize Large Images* — toggle to limit max width via `max-w`
68
+
69
+ - **Resize Dimensions** (visible when resize is enabled)
70
+ - *Large Image Max Width (px)* and *Very Large Image Max Width (px)*
71
+
72
+ - **Compression Settings**
73
+ - *Large Image Quality* / *Very Large Image Quality* — `q` value 0–100 (hidden in lossless mode)
74
+
75
+ - **Advanced Options**
76
+ - *Use Lossless Compression* (`lossless=1`)
77
+ - *Use DPR Optimization* (`dpr=2` for Retina)
78
+ - *Enhanced Chroma Sampling* (`chromasub=444` for JPEGs)
79
+ - *Preserve Color Profiles* (`cs=origin`)
80
+
81
+ A *Restore Defaults* button is available at any time.
47
82
 
48
83
  ### Asset Filtering & Optimization
49
84
 
@@ -69,6 +104,7 @@ This approach allows you to safely experiment with different optimization parame
69
104
  - Mass-update existing media libraries with optimized assets
70
105
  - Apply consistent optimization settings across your entire asset collection
71
106
  - Save time compared to manual optimization workflows
107
+ - Up to 10 assets are processed in parallel for faster runs
72
108
 
73
109
  ## Development
74
110
 
@@ -0,0 +1 @@
1
+ import{b as y,A as g}from"./index-PtBA-34N.js";const w=t=>new Promise(e=>setTimeout(e,t)),R=(t,e,s,r)=>{const c=e*r**t;return Math.min(c,s)},C=(t,e)=>{const s=t?Number.parseInt(t,10):0;return s>0?s*1e3:R(e.retryCount,e.initialRetryDelay,e.maxRetryDelay,e.retryBackoffFactor)},q=(t,e,s)=>y({apiToken:t,environment:e,autoRetry:!1,...s?{baseUrl:s}:{}}),v=t=>t instanceof g&&t.response.status===429,b=t=>t.response.headers["retry-after"]??t.response.headers["x-ratelimit-reset"],$=t=>{const e={};for(const[s,r]of Object.entries(t))typeof r=="string"?e[s]=r:(typeof r=="number"||typeof r=="boolean")&&(e[s]=String(r));return e},A=async(t,e,s)=>{const r=await fetch(t);if(!r.ok)throw new Error(`Failed to fetch image from URL: ${r.status} ${r.statusText}`);const l=await(await r.blob()).arrayBuffer(),i=new Uint8Array(l),a=await fetch(e,{method:"PUT",headers:{...s,"Content-Length":i.length.toString()},body:i});if(!a.ok)throw new Error(`Failed to upload file to S3: ${a.status} ${a.statusText}`)};async function p(t,e,s,r,c,l,i=0,a=1e3,u=6e4,h=2){if(console.log(`Replacing DatoCMS asset ID ${t} with image from URL: ${e}${i>0?` (retry ${i})`:""}`),!t||!s)throw new Error("Missing required parameters: assetId and apiToken are required");const d={retryCount:i,initialRetryDelay:a,maxRetryDelay:u,retryBackoffFactor:h},m=async(o=1)=>p(t,e,s,r,c,l,i+o,a,u,h);try{const o=q(s,r,c),n=await o.uploadRequest.create({filename:l||"optimized-image.jpg"});await A(e,n.url,$(n.request_headers));const f=await o.uploads.rawUpdate(t,{data:{id:t,type:"upload",attributes:{path:n.id}}});return console.log("Asset replaced successfully:",f),f}catch(o){if(v(o)&&i<5){const n=C(b(o),d);return console.error(`Rate limit exceeded while replacing asset. Retrying in ${n}ms...`),await w(n),m()}throw console.error("Error replacing asset:",o),o}}class D{queue=[];activeCount=0;config;processing=!1;completedCount=0;failedCount=0;resolvePromise=null;constructor(e){this.config={...e,concurrency:e.concurrency||3,initialRetryDelay:e.initialRetryDelay||1e3,maxRetryDelay:e.maxRetryDelay||6e4,retryBackoffFactor:e.retryBackoffFactor||2}}addTask(e,s,r){this.queue.push({assetId:e,newImageUrl:s,filename:r,retryCount:0})}async processTask(e){try{await p(e.assetId,e.newImageUrl,this.config.apiToken,this.config.environment,this.config.baseUrl,e.filename,e.retryCount,this.config.initialRetryDelay,this.config.maxRetryDelay,this.config.retryBackoffFactor),this.completedCount++}catch(s){console.error(`Failed to replace asset ${e.assetId} after multiple retries:`,s),this.failedCount++}finally{this.activeCount--,this.processQueue()}}processQueue(){if(this.queue.length===0&&this.activeCount===0&&this.resolvePromise){this.resolvePromise({succeeded:this.completedCount,failed:this.failedCount}),this.resolvePromise=null,this.processing=!1;return}for(;this.queue.length>0&&this.activeCount<(this.config.concurrency||3);){const e=this.queue.shift();e&&(this.activeCount++,this.processTask(e))}}start(){return this.processing?Promise.reject(new Error("Asset replacer is already processing")):(this.processing=!0,this.completedCount=0,this.failedCount=0,new Promise(e=>{this.resolvePromise=e,this.processQueue()}))}getStatus(){return{queued:this.queue.length,active:this.activeCount,completed:this.completedCount,failed:this.failedCount}}}export{D as AssetReplacer,p as default};