ipx 4.0.0-beta.1 → 4.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 CHANGED
@@ -15,10 +15,8 @@ Point IPX at a directory or a list of allowed domains, and every image is availa
15
15
  /w_512,f_webp/photos/buffalo.png
16
16
  ```
17
17
 
18
- Used by [Nuxt Image](https://image.nuxt.com/) and [Netlify](https://www.npmjs.com/package/@netlify/ipx) and open to everyone!
19
-
20
18
  > [!NOTE]
21
- > This is the active development branch for IPX v4. Check out [v3](https://github.com/unjs/ipx/tree/v3) for v3 docs, and [Migration from v3 to v4](#migration-from-v3-to-v4) if you are upgrading.
19
+ > This is the active development branch for IPX v4. Check out [v3](https://github.com/unjs/ipx/tree/v3) for v3 docs, and release notes ([alpha](https://github.com/unjs/ipx/releases/tag/v4.0.0-alpha.1), [beta](https://github.com/unjs/ipx/releases/tag/v4.0.0-beta.1)) if you are upgrading.
22
20
 
23
21
  ## Quick Start
24
22
 
@@ -68,318 +66,318 @@ Colours (`background`, `tint`) accept any colour sharp understands: hex (`f00`,
68
66
 
69
67
  <table>
70
68
  <tr>
71
- <td valign="top" width="50%">
72
- <div align="center"><img src="./assets/operations/width.jpg" alt="/w_160/sample.jpg"></div>
73
- <div align="center"><b><code>width</code> / <code>w</code></b></div>
74
- <code>/w_160/sample.jpg</code>
69
+ <td valign="top" align="left" width="50%">
70
+ <div align="center"><img src="./assets/operations/width.jpg?v=db01a531" width="160" alt="/w_160/scene.jpg"></div>
71
+ <div><b><code>width</code> / <code>w</code></b></div>
72
+ <code>/w_160/scene.jpg</code>
75
73
  <br>
76
- Positive integer. (<a href="https://sharp.pixelplumbing.com/api-resize#resize">docs</a>)
74
+ Resize to a width in pixels, a positive integer. The height follows the aspect ratio. (<a href="https://sharp.pixelplumbing.com/api-resize#resize">docs</a>)
77
75
  </td>
78
- <td valign="top" width="50%">
79
- <div align="center"><img src="./assets/operations/height.jpg" alt="/h_120/sample.jpg"></div>
80
- <div align="center"><b><code>height</code> / <code>h</code></b></div>
81
- <code>/h_120/sample.jpg</code>
76
+ <td valign="top" align="left" width="50%">
77
+ <div align="center"><img src="./assets/operations/height.jpg?v=ee67aac0" width="180" alt="/h_120/scene.jpg"></div>
78
+ <div><b><code>height</code> / <code>h</code></b></div>
79
+ <code>/h_120/scene.jpg</code>
82
80
  <br>
83
- Positive integer. (<a href="https://sharp.pixelplumbing.com/api-resize#resize">docs</a>)
81
+ Resize to a height in pixels, a positive integer. The width follows the aspect ratio. (<a href="https://sharp.pixelplumbing.com/api-resize#resize">docs</a>)
84
82
  </td>
85
83
  </tr>
86
84
  <tr>
87
- <td valign="top" width="50%">
88
- <div align="center"><img src="./assets/operations/resize.jpg" alt="/s_200x200/sample.jpg"></div>
89
- <div align="center"><b><code>resize</code> / <code>s</code></b></div>
90
- <code>/s_200x200/sample.jpg</code>
85
+ <td valign="top" align="left" width="50%">
86
+ <div align="center"><img src="./assets/operations/resize.jpg?v=99dda472" width="200" alt="/s_200x200/scene.jpg"></div>
87
+ <div><b><code>resize</code> / <code>s</code></b></div>
88
+ <code>/s_200x200/scene.jpg</code>
91
89
  <br>
92
- <code>{width}x{height}</code> of positive integers. A single value (<code>s_200</code>) is a square. (<a href="https://sharp.pixelplumbing.com/api-resize#resize">docs</a>)
90
+ Resize to <code>{width}x{height}</code>, both positive integers. A single value (<code>s_200</code>) is a square. (<a href="https://sharp.pixelplumbing.com/api-resize#resize">docs</a>)
93
91
  </td>
94
- <td valign="top" width="50%">
95
- <div align="center"><img src="./assets/operations/kernel.jpg" alt="/s_80x80,kernel_nearest/sample.jpg"></div>
96
- <div align="center"><b><code>kernel</code></b></div>
97
- <code>/s_80x80,kernel_nearest/sample.jpg</code>
92
+ <td valign="top" align="left" width="50%">
93
+ <div align="center"><img src="./assets/operations/kernel.jpg?v=96e57f6e" width="80" alt="/s_80x80,kernel_nearest/photo.jpg"></div>
94
+ <div><b><code>kernel</code></b></div>
95
+ <code>/s_80x80,kernel_nearest/photo.jpg</code>
98
96
  <br>
99
97
  Sets <code>kernel</code> option for <code>resize</code>. One of <code>nearest</code>, <code>linear</code>, <code>cubic</code>, <code>mitchell</code>, <code>lanczos2</code>, <code>lanczos3</code> (default), <code>mks2013</code> or <code>mks2021</code>. (<a href="https://sharp.pixelplumbing.com/api-resize#resize">docs</a>)
100
98
  </td>
101
99
  </tr>
102
100
  <tr>
103
- <td valign="top" width="50%">
104
- <div align="center"><img src="./assets/operations/fit.jpg" alt="/s_200x200,fit_contain,b_00ff00/sample.jpg"></div>
105
- <div align="center"><b><code>fit</code></b></div>
106
- <code>/s_200x200,fit_contain,b_00ff00/sample.jpg</code>
101
+ <td valign="top" align="left" width="50%">
102
+ <div align="center"><img src="./assets/operations/fit.jpg?v=08b8660c" width="300" alt="/s_300x150,fit_contain,b_00ff00/scene.jpg"></div>
103
+ <div><b><code>fit</code></b></div>
104
+ <code>/s_300x150,fit_contain,b_00ff00/scene.jpg</code>
107
105
  <br>
108
106
  Sets <code>fit</code> option for <code>resize</code>. One of <code>contain</code>, <code>cover</code> (default), <code>fill</code>, <code>inside</code> or <code>outside</code>. (<a href="https://sharp.pixelplumbing.com/api-resize#resize">docs</a>)
109
107
  </td>
110
- <td valign="top" width="50%">
111
- <div align="center"><img src="./assets/operations/position.jpg" alt="/s_200x200,pos_top/sample.jpg"></div>
112
- <div align="center"><b><code>position</code> / <code>pos</code></b></div>
113
- <code>/s_200x200,pos_top/sample.jpg</code>
108
+ <td valign="top" align="left" width="50%">
109
+ <div align="center"><img src="./assets/operations/position.jpg?v=187fe5e7" width="107" alt="/s_150x300,pos_top/scene.jpg"></div>
110
+ <div><b><code>position</code> / <code>pos</code></b></div>
111
+ <code>/s_150x300,pos_top/scene.jpg</code>
114
112
  <br>
115
- Sets <code>position</code> option for <code>resize</code>. A position (<code>top</code>, <code>right top</code>, <code>right</code>, <code>right bottom</code>, <code>bottom</code>, <code>left bottom</code>, <code>left</code>, <code>left top</code>), gravity (<code>north</code>, <code>northeast</code>, ..., <code>center</code>) or strategy (<code>entropy</code>, <code>attention</code>). Since <code>_</code> separates arguments, multi-word values use <code>-</code> (<code>pos_right-top</code>). The numeric gravity (<code>0</code>-<code>8</code>) and strategy (<code>16</code>-<code>17</code>) constants are also accepted. (<a href="https://sharp.pixelplumbing.com/api-resize#resize">docs</a>)
113
+ Sets <code>position</code> option for <code>resize</code>. A position (<code>top</code>, <code>right top</code>, ..., <code>left top</code>), gravity (<code>north</code>, <code>northeast</code>, ..., <code>center</code>) or strategy (<code>entropy</code>, <code>attention</code>), also accepted as their numeric constants. Since <code>_</code> separates arguments, multi-word values use <code>-</code> (<code>pos_right-top</code>). (<a href="https://sharp.pixelplumbing.com/api-resize#resize">docs</a>)
116
114
  </td>
117
115
  </tr>
118
116
  <tr>
119
- <td valign="top" width="50%">
120
- <div align="center"><img src="./assets/operations/extend.jpg" alt="/extend_20_40_20_40_mirror/sample.jpg"></div>
121
- <div align="center"><b><code>extend</code></b></div>
122
- <code>/extend_20_40_20_40_mirror/sample.jpg</code>
117
+ <td valign="top" align="left" width="50%">
118
+ <div align="center"><img src="./assets/operations/extend.jpg?v=0dfb2098" width="320" alt="/extend_20_40_20_40_mirror/scene.jpg"></div>
119
+ <div><b><code>extend</code></b></div>
120
+ <code>/extend_20_40_20_40_mirror/scene.jpg</code>
123
121
  <br>
124
- Extend / pad / extrude one or more edges of the image. Format: <code>extend_{top}_{right}_{bottom}_{left}_{extendWith}</code>. Edges are integers between <code>0</code> and <code>10000</code>. Optional <code>extendWith</code>: <code>background</code> (default), <code>copy</code>, <code>repeat</code> or <code>mirror</code>. When extending with <code>background</code>, the colour comes from the <code>background</code> / <code>b</code> modifier, e.g. <code>b_00ff00,extend_20_40_20_40</code>. (<a href="https://sharp.pixelplumbing.com/api-resize#extend">docs</a>)
122
+ Pad or extrude the edges, as <code>extend_{top}_{right}_{bottom}_{left}</code> with an optional <code>{extendWith}</code>. Edges are integers between <code>0</code> and <code>10000</code>. <code>extendWith</code> is <code>background</code> (default), <code>copy</code>, <code>repeat</code> or <code>mirror</code>; <code>background</code> takes its colour from the <code>background</code> / <code>b</code> modifier. (<a href="https://sharp.pixelplumbing.com/api-resize#extend">docs</a>)
125
123
  </td>
126
- <td valign="top" width="50%">
127
- <div align="center"><img src="./assets/operations/background.jpg" alt="/rotate_45,b_00ff00/sample.jpg"></div>
128
- <div align="center"><b><code>background</code> / <code>b</code></b></div>
129
- <code>/rotate_45,b_00ff00/sample.jpg</code>
124
+ <td valign="top" align="left" width="50%">
125
+ <div align="center"><img src="./assets/operations/background.jpg?v=6b197635" width="320" alt="/rotate_45,b_00ff00/scene.jpg"></div>
126
+ <div><b><code>background</code> / <code>b</code></b></div>
127
+ <code>/rotate_45,b_00ff00/scene.jpg</code>
130
128
  <br>
131
- Background colour used by <code>extend</code>, <code>rotate</code>, <code>flatten</code>, <code>opacity</code> and <code>resize</code> (with <code>fit_contain</code>).
129
+ Background colour, a hex (<code>f00</code>, <code>ff0000</code>) or named (<code>red</code>) colour. Used by <code>extend</code>, <code>rotate</code>, <code>flatten</code>, <code>opacity</code> and <code>resize</code> (with <code>fit_contain</code>).
132
130
  </td>
133
131
  </tr>
134
132
  <tr>
135
- <td valign="top" width="50%">
136
- <div align="center"><img src="./assets/operations/extract.jpg" alt="/extract_60_20_180_140/sample.jpg"></div>
137
- <div align="center"><b><code>extract</code> / <code>crop</code></b></div>
138
- <code>/extract_60_20_180_140/sample.jpg</code>
133
+ <td valign="top" align="left" width="50%">
134
+ <div align="center"><img src="./assets/operations/extract.jpg?v=ac75bdaa" width="160" alt="/extract_150_10_160_120/scene.jpg"></div>
135
+ <div><b><code>extract</code> / <code>crop</code></b></div>
136
+ <code>/extract_150_10_160_120/scene.jpg</code>
139
137
  <br>
140
- Extract/crop a region of the image, as <code>extract_{left}_{top}_{width}_{height}</code>. All four arguments are required; <code>width</code> and <code>height</code> must be positive. (<a href="https://sharp.pixelplumbing.com/api-resize#extract">docs</a>)
138
+ Crop a region, as <code>extract_{left}_{top}_{width}_{height}</code>. All four arguments are required; <code>width</code> and <code>height</code> must be positive. (<a href="https://sharp.pixelplumbing.com/api-resize#extract">docs</a>)
141
139
  </td>
142
- <td valign="top" width="50%">
143
- <div align="center"><img src="./assets/operations/format.webp" alt="/f_webp/sample.jpg"></div>
144
- <div align="center"><b><code>format</code> / <code>f</code></b></div>
145
- <code>/f_webp/sample.jpg</code>
140
+ <td valign="top" align="left" width="50%">
141
+ <div align="center"><img src="./assets/operations/format.webp?v=90841432" width="320" alt="/f_webp/photo.jpg"></div>
142
+ <div><b><code>format</code> / <code>f</code></b></div>
143
+ <code>/f_webp/photo.jpg</code>
146
144
  <br>
147
- Supported formats: <code>jpg</code>, <code>jpeg</code>, <code>png</code>, <code>webp</code>, <code>avif</code>, <code>gif</code>, <code>heif</code>, <code>tiff</code> and <code>auto</code> (experimental, only with middleware). (<a href="https://sharp.pixelplumbing.com/api-output#toformat">docs</a>)
145
+ Output format. One of <code>jpg</code>, <code>jpeg</code>, <code>png</code>, <code>webp</code>, <code>avif</code>, <code>gif</code>, <code>heif</code>, <code>tiff</code> or <code>auto</code> (experimental, only with middleware). (<a href="https://sharp.pixelplumbing.com/api-output#toformat">docs</a>)
148
146
  </td>
149
147
  </tr>
150
148
  <tr>
151
- <td valign="top" width="50%">
152
- <div align="center"><img src="./assets/operations/quality.jpg" alt="/q_10/sample.jpg"></div>
153
- <div align="center"><b><code>quality</code> / <code>q</code></b></div>
154
- <code>/q_10/sample.jpg</code>
149
+ <td valign="top" align="left" width="50%">
150
+ <div align="center"><img src="./assets/operations/quality.jpg?v=865c529f" width="320" alt="/q_10/photo.jpg"></div>
151
+ <div><b><code>quality</code> / <code>q</code></b></div>
152
+ <code>/q_10/photo.jpg</code>
155
153
  <br>
156
- Integer between <code>1</code> and <code>100</code>.
154
+ Encoding quality, an integer between <code>1</code> and <code>100</code>. A lower value means a smaller file.
157
155
  </td>
158
- <td valign="top" width="50%">
159
- <div align="center"><img src="./assets/operations/rotate.jpg" alt="/rotate_45/sample.jpg"></div>
160
- <div align="center"><b><code>rotate</code></b></div>
161
- <code>/rotate_45/sample.jpg</code>
156
+ <td valign="top" align="left" width="50%">
157
+ <div align="center"><img src="./assets/operations/rotate.jpg?v=4bc4f0b2" width="320" alt="/rotate_45/scene.jpg"></div>
158
+ <div><b><code>rotate</code></b></div>
159
+ <code>/rotate_45/scene.jpg</code>
162
160
  <br>
163
- Angle in degrees, between <code>-3600</code> and <code>3600</code>. Angles that are not a multiple of <code>90</code> are filled with the <code>background</code> / <code>b</code> colour. Without an angle (<code>rotate</code>) the image is auto-oriented from its EXIF tag. (<a href="https://sharp.pixelplumbing.com/api-operation#rotate">docs</a>)
161
+ Angle in degrees, between <code>-3600</code> and <code>3600</code>. Angles that are not a multiple of <code>90</code> fill the corners with the <code>background</code> / <code>b</code> colour. Without an angle (<code>rotate</code>) the image is auto-oriented from its EXIF tag. (<a href="https://sharp.pixelplumbing.com/api-operation#rotate">docs</a>)
164
162
  </td>
165
163
  </tr>
166
164
  <tr>
167
- <td valign="top" width="50%">
168
- <div align="center"><img src="./assets/operations/enlarge.jpg" alt="/enlarge,s_400x400/sample.jpg"></div>
169
- <div align="center"><b><code>enlarge</code></b></div>
170
- <code>/enlarge,s_400x400/sample.jpg</code>
165
+ <td valign="top" align="left" width="50%">
166
+ <div align="center"><img src="./assets/operations/enlarge.jpg?v=9c6fb296" width="320" alt="/enlarge,s_400x400/photo.jpg"></div>
167
+ <div><b><code>enlarge</code></b></div>
168
+ <code>/enlarge,s_400x400/photo.jpg</code>
171
169
  <br>
172
- Allow the image to be upscaled. By default the returned image will never be larger than the source in any dimension, while preserving the requested aspect ratio.
170
+ Allow the image to be upscaled (<code>enlarge_false</code> turns it back off). Without it, the source is never upscaled: a requested box that is too large is shrunk, preserving its aspect ratio, so the output is never larger than the source, except along the padded axis of <code>fit_contain</code>.
173
171
  </td>
174
- <td valign="top" width="50%">
175
- <div align="center"><img src="./assets/operations/flip.jpg" alt="/flip/sample.jpg"></div>
176
- <div align="center"><b><code>flip</code></b></div>
177
- <code>/flip/sample.jpg</code>
172
+ <td valign="top" align="left" width="50%">
173
+ <div align="center"><img src="./assets/operations/flip.jpg?v=40a31bca" width="320" alt="/flip/scene.jpg"></div>
174
+ <div><b><code>flip</code></b></div>
175
+ <code>/flip/scene.jpg</code>
178
176
  <br>
179
- Mirror the image vertically. (<a href="https://sharp.pixelplumbing.com/api-operation#flip">docs</a>)
177
+ Mirror the image vertically, about the horizontal axis. Combine with <code>flop</code> to turn it upside down. (<a href="https://sharp.pixelplumbing.com/api-operation#flip">docs</a>)
180
178
  </td>
181
179
  </tr>
182
180
  <tr>
183
- <td valign="top" width="50%">
184
- <div align="center"><img src="./assets/operations/flop.jpg" alt="/flop/sample.jpg"></div>
185
- <div align="center"><b><code>flop</code></b></div>
186
- <code>/flop/sample.jpg</code>
181
+ <td valign="top" align="left" width="50%">
182
+ <div align="center"><img src="./assets/operations/flop.jpg?v=747fa554" width="320" alt="/flop/scene.jpg"></div>
183
+ <div><b><code>flop</code></b></div>
184
+ <code>/flop/scene.jpg</code>
187
185
  <br>
188
- Mirror the image horizontally. (<a href="https://sharp.pixelplumbing.com/api-operation#flop">docs</a>)
186
+ Mirror the image horizontally, about the vertical axis. Combine with <code>flip</code> to turn it upside down. (<a href="https://sharp.pixelplumbing.com/api-operation#flop">docs</a>)
189
187
  </td>
190
- <td valign="top" width="50%">
191
- <div align="center"><img src="./assets/operations/sharpen.jpg" alt="/sharpen_5/sample.jpg"></div>
192
- <div align="center"><b><code>sharpen</code></b></div>
193
- <code>/sharpen_5/sample.jpg</code>
188
+ <td valign="top" align="left" width="50%">
189
+ <div align="center"><img src="./assets/operations/sharpen.jpg?v=eafda8ec" width="320" alt="/sharpen_5/photo.jpg"></div>
190
+ <div><b><code>sharpen</code></b></div>
191
+ <code>/sharpen_5/photo.jpg</code>
194
192
  <br>
195
- <code>{sigma}_{flat}_{jagged}_{x1}_{y2}_{y3}</code>. <code>sigma</code> is between <code>0.000001</code> and <code>10</code>, the rest between <code>0</code> and <code>1000000</code>. Without arguments (<code>sharpen</code>) a mild sharpen is applied. (<a href="https://sharp.pixelplumbing.com/api-operation#sharpen">docs</a>)
193
+ Sharpen the image, as <code>{sigma}_{flat}_{jagged}_{x1}_{y2}_{y3}</code>. <code>sigma</code> is a number between <code>0.000001</code> and <code>10</code>, the rest between <code>0</code> and <code>1000000</code>. Without arguments (<code>sharpen</code>) a mild sharpen is applied. (<a href="https://sharp.pixelplumbing.com/api-operation#sharpen">docs</a>)
196
194
  </td>
197
195
  </tr>
198
196
  <tr>
199
- <td valign="top" width="50%">
200
- <div align="center"><img src="./assets/operations/median.jpg" alt="/median_10/sample.jpg"></div>
201
- <div align="center"><b><code>median</code></b></div>
202
- <code>/median_10/sample.jpg</code>
197
+ <td valign="top" align="left" width="50%">
198
+ <div align="center"><img src="./assets/operations/median.jpg?v=2317e13b" width="320" alt="/median_10/photo.jpg"></div>
199
+ <div><b><code>median</code></b></div>
200
+ <code>/median_10/photo.jpg</code>
203
201
  <br>
204
- Square mask size, an integer between <code>1</code> and <code>1000</code> (defaults to <code>3</code>). (<a href="https://sharp.pixelplumbing.com/api-operation#median">docs</a>)
202
+ Apply a median filter, removing noise while keeping edges. Square mask size, an integer between <code>1</code> and <code>1000</code> (defaults to <code>3</code>). (<a href="https://sharp.pixelplumbing.com/api-operation#median">docs</a>)
205
203
  </td>
206
- <td valign="top" width="50%">
207
- <div align="center"><img src="./assets/operations/blur.jpg" alt="/blur_5/sample.jpg"></div>
208
- <div align="center"><b><code>blur</code></b></div>
209
- <code>/blur_5/sample.jpg</code>
204
+ <td valign="top" align="left" width="50%">
205
+ <div align="center"><img src="./assets/operations/blur.jpg?v=565d775a" width="320" alt="/blur_5/edges.jpg"></div>
206
+ <div><b><code>blur</code></b></div>
207
+ <code>/blur_5/edges.jpg</code>
210
208
  <br>
211
- <code>{sigma}_{precision}_{minAmplitude}</code>. <code>sigma</code> is a number between <code>0.3</code> and <code>1000</code>, <code>precision</code> one of <code>integer</code> (default), <code>float</code> or <code>approximate</code>, <code>minAmplitude</code> a number between <code>0.001</code> and <code>1</code>. Without arguments (<code>blur</code>) a mild blur is applied. (<a href="https://sharp.pixelplumbing.com/api-operation#blur">docs</a>)
209
+ Gaussian blur, as <code>{sigma}_{precision}_{minAmplitude}</code>. <code>sigma</code> is a number between <code>0.3</code> and <code>1000</code>, <code>precision</code> one of <code>integer</code> (default), <code>float</code> or <code>approximate</code>, <code>minAmplitude</code> between <code>0.001</code> and <code>1</code>. Without arguments (<code>blur</code>) a mild blur is applied. (<a href="https://sharp.pixelplumbing.com/api-operation#blur">docs</a>)
212
210
  </td>
213
211
  </tr>
214
212
  <tr>
215
- <td valign="top" width="50%">
216
- <div align="center"><img src="./assets/operations/dilate.png" alt="/dilate_5/sample-logo.png"></div>
217
- <div align="center"><b><code>dilate</code></b></div>
218
- <code>/dilate_5/sample-logo.png</code>
213
+ <td valign="top" align="left" width="50%">
214
+ <div align="center"><img src="./assets/operations/dilate.png?v=17bf0c5d" width="320" alt="/dilate_4/graphic.png"></div>
215
+ <div><b><code>dilate</code></b></div>
216
+ <code>/dilate_4/graphic.png</code>
219
217
  <br>
220
- Expand foreground objects. Width in pixels, an integer between <code>1</code> and <code>100</code> (defaults to <code>1</code>). Capped below the sharp maximum, since the cost grows with the width. (<a href="https://sharp.pixelplumbing.com/api-operation#dilate">docs</a>)
218
+ Expand foreground objects. Width in pixels, an integer between <code>1</code> and <code>100</code> (defaults to <code>1</code>), capped below the sharp maximum since the cost grows with the width. (<a href="https://sharp.pixelplumbing.com/api-operation#dilate">docs</a>)
221
219
  </td>
222
- <td valign="top" width="50%">
223
- <div align="center"><img src="./assets/operations/erode.png" alt="/erode_5/sample-logo.png"></div>
224
- <div align="center"><b><code>erode</code></b></div>
225
- <code>/erode_5/sample-logo.png</code>
220
+ <td valign="top" align="left" width="50%">
221
+ <div align="center"><img src="./assets/operations/erode.png?v=b5c7e49a" width="320" alt="/erode_4/graphic.png"></div>
222
+ <div><b><code>erode</code></b></div>
223
+ <code>/erode_4/graphic.png</code>
226
224
  <br>
227
- Shrink foreground objects. Width in pixels, an integer between <code>1</code> and <code>100</code> (defaults to <code>1</code>). Capped below the sharp maximum, since the cost grows with the width. (<a href="https://sharp.pixelplumbing.com/api-operation#erode">docs</a>)
225
+ Shrink foreground objects. Width in pixels, an integer between <code>1</code> and <code>100</code> (defaults to <code>1</code>), capped below the sharp maximum since the cost grows with the width. (<a href="https://sharp.pixelplumbing.com/api-operation#erode">docs</a>)
228
226
  </td>
229
227
  </tr>
230
228
  <tr>
231
- <td valign="top" width="50%">
232
- <div align="center"><img src="./assets/operations/clahe.jpg" alt="/clahe_20_20_5/sample.jpg"></div>
233
- <div align="center"><b><code>clahe</code></b></div>
234
- <code>/clahe_20_20_5/sample.jpg</code>
229
+ <td valign="top" align="left" width="50%">
230
+ <div align="center"><img src="./assets/operations/clahe.jpg?v=0aa709f5" width="320" alt="/clahe_20_20_5/edges.jpg"></div>
231
+ <div><b><code>clahe</code></b></div>
232
+ <code>/clahe_20_20_5/edges.jpg</code>
235
233
  <br>
236
- Contrast limiting adaptive histogram equalization, as <code>{width}_{height}_{maxSlope}</code>. <code>width</code> is required and <code>height</code> defaults to it (a square window); both are integers between <code>1</code> and <code>100</code>, capped below the sharp maximum since the cost grows with the window, and clamped to the source dimensions. <code>maxSlope</code> is an integer between <code>0</code> and <code>100</code> (defaults to <code>3</code>). (<a href="https://sharp.pixelplumbing.com/api-operation#clahe">docs</a>)
234
+ Contrast limiting adaptive histogram equalization, as <code>{width}_{height}_{maxSlope}</code>. <code>width</code> is required and <code>height</code> defaults to it (a square window); both are integers between <code>1</code> and <code>100</code>, clamped to the source dimensions. <code>maxSlope</code> is an integer between <code>0</code> and <code>100</code> (defaults to <code>3</code>). (<a href="https://sharp.pixelplumbing.com/api-operation#clahe">docs</a>)
237
235
  </td>
238
- <td valign="top" width="50%">
239
- <div align="center"><img src="./assets/operations/flatten.jpg" alt="/flatten,b_00ff00,f_jpeg/sample-alpha.png"></div>
240
- <div align="center"><b><code>flatten</code></b></div>
241
- <code>/flatten,b_00ff00,f_jpeg/sample-alpha.png</code>
236
+ <td valign="top" align="left" width="50%">
237
+ <div align="center"><img src="./assets/operations/flatten.jpg?v=02455cd1" width="320" alt="/flatten,b_00ff00,f_jpeg/alpha.png"></div>
238
+ <div><b><code>flatten</code></b></div>
239
+ <code>/flatten,b_00ff00,f_jpeg/alpha.png</code>
242
240
  <br>
243
- Remove alpha channel, if any, and replace with the <code>background</code> / <code>b</code> colour. (<a href="https://sharp.pixelplumbing.com/api-operation#flatten">docs</a>)
241
+ Remove the alpha channel, if any, and replace transparency with the <code>background</code> / <code>b</code> colour. (<a href="https://sharp.pixelplumbing.com/api-operation#flatten">docs</a>)
244
242
  </td>
245
243
  </tr>
246
244
  <tr>
247
- <td valign="top" width="50%">
248
- <div align="center"><img src="./assets/operations/unflatten.webp" alt="/unflatten,f_webp/sample-logo.png"></div>
249
- <div align="center"><b><code>unflatten</code></b></div>
250
- <code>/unflatten,f_webp/sample-logo.png</code>
245
+ <td valign="top" align="left" width="50%">
246
+ <div align="center"><img src="./assets/operations/unflatten.webp?v=db6b8455" width="320" alt="/unflatten,f_webp/graphic.png"></div>
247
+ <div><b><code>unflatten</code></b></div>
248
+ <code>/unflatten,f_webp/graphic.png</code>
251
249
  <br>
252
250
  Make every fully white pixel transparent, so the output format needs an alpha channel (<code>png</code>, <code>webp</code>, <code>avif</code>). (<a href="https://sharp.pixelplumbing.com/api-operation#unflatten">docs</a>)
253
251
  </td>
254
- <td valign="top" width="50%">
255
- <div align="center"><img src="./assets/operations/gamma.jpg" alt="/gamma_3/sample.jpg"></div>
256
- <div align="center"><b><code>gamma</code></b></div>
257
- <code>/gamma_3/sample.jpg</code>
252
+ <td valign="top" align="left" width="50%">
253
+ <div align="center"><img src="./assets/operations/gamma.jpg?v=ec2a179b" width="320" alt="/gamma_3/colour.jpg"></div>
254
+ <div><b><code>gamma</code></b></div>
255
+ <code>/gamma_3/colour.jpg</code>
258
256
  <br>
259
- <code>{gamma}_{gammaOut}</code>, each a number between <code>1.0</code> and <code>3.0</code> (defaults to <code>2.2</code>). (<a href="https://sharp.pixelplumbing.com/api-operation#gamma">docs</a>)
257
+ Gamma correction, as <code>{gamma}_{gammaOut}</code>, each a number between <code>1.0</code> and <code>3.0</code> (defaults to <code>2.2</code>). (<a href="https://sharp.pixelplumbing.com/api-operation#gamma">docs</a>)
260
258
  </td>
261
259
  </tr>
262
260
  <tr>
263
- <td valign="top" width="50%">
264
- <div align="center"><img src="./assets/operations/negate.jpg" alt="/negate/sample.jpg"></div>
265
- <div align="center"><b><code>negate</code></b></div>
266
- <code>/negate/sample.jpg</code>
261
+ <td valign="top" align="left" width="50%">
262
+ <div align="center"><img src="./assets/operations/negate.jpg?v=6dcd6c51" width="320" alt="/negate/colour.jpg"></div>
263
+ <div><b><code>negate</code></b></div>
264
+ <code>/negate/colour.jpg</code>
267
265
  <br>
268
- Optional <code>{alpha}</code> (<code>true</code> by default) controls whether the alpha channel is negated too, e.g. <code>negate_false</code>. (<a href="https://sharp.pixelplumbing.com/api-operation#negate">docs</a>)
266
+ Produce the negative of the image. Optional <code>{alpha}</code> (<code>true</code> by default) controls whether the alpha channel is negated too, e.g. <code>negate_false</code>. (<a href="https://sharp.pixelplumbing.com/api-operation#negate">docs</a>)
269
267
  </td>
270
- <td valign="top" width="50%">
271
- <div align="center"><img src="./assets/operations/normalize.jpg" alt="/normalize_10_90/sample.jpg"></div>
272
- <div align="center"><b><code>normalize</code></b></div>
273
- <code>/normalize_10_90/sample.jpg</code>
268
+ <td valign="top" align="left" width="50%">
269
+ <div align="center"><img src="./assets/operations/normalize.jpg?v=7ab13cca" width="320" alt="/normalize_10_90/edges.jpg"></div>
270
+ <div><b><code>normalize</code></b></div>
271
+ <code>/normalize_10_90/edges.jpg</code>
274
272
  <br>
275
- Stretch the luminance to the full dynamic range, as <code>{lower}_{upper}</code> percentiles. <code>lower</code> is a number between <code>0</code> and <code>99</code> (defaults to <code>1</code>), <code>upper</code> between <code>1</code> and <code>100</code> (defaults to <code>99</code>) and has to be greater than <code>lower</code>. (<a href="https://sharp.pixelplumbing.com/api-operation#normalize">docs</a>)
273
+ Stretch the luminance to the full dynamic range, as <code>{lower}_{upper}</code> percentiles. <code>lower</code> is a number between <code>0</code> and <code>99</code> (defaults to <code>1</code>), <code>upper</code> between <code>1</code> and <code>100</code> (defaults to <code>99</code>) and greater than <code>lower</code>. (<a href="https://sharp.pixelplumbing.com/api-operation#normalize">docs</a>)
276
274
  </td>
277
275
  </tr>
278
276
  <tr>
279
- <td valign="top" width="50%">
280
- <div align="center"><img src="./assets/operations/threshold.jpg" alt="/threshold_128/sample.jpg"></div>
281
- <div align="center"><b><code>threshold</code></b></div>
282
- <code>/threshold_128/sample.jpg</code>
277
+ <td valign="top" align="left" width="50%">
278
+ <div align="center"><img src="./assets/operations/threshold.jpg?v=e870d914" width="320" alt="/threshold_128/colour.jpg"></div>
279
+ <div><b><code>threshold</code></b></div>
280
+ <code>/threshold_128/colour.jpg</code>
283
281
  <br>
284
- <code>{threshold}_{grayscale}</code>. <code>threshold</code> is an integer between <code>0</code> and <code>255</code> (defaults to <code>128</code>). Optional <code>grayscale</code> (<code>true</code> by default) converts to single channel grayscale first, e.g. <code>threshold_128_false</code>. (<a href="https://sharp.pixelplumbing.com/api-operation#threshold">docs</a>)
282
+ Map every pixel to black or white, as <code>{threshold}_{grayscale}</code>. <code>threshold</code> is an integer between <code>0</code> and <code>255</code> (defaults to <code>128</code>). Optional <code>grayscale</code> (<code>true</code> by default) converts to single channel first, e.g. <code>threshold_128_false</code>. (<a href="https://sharp.pixelplumbing.com/api-operation#threshold">docs</a>)
285
283
  </td>
286
- <td valign="top" width="50%">
287
- <div align="center"><img src="./assets/operations/linear.jpg" alt="/linear_1.5_-30/sample.jpg"></div>
288
- <div align="center"><b><code>linear</code></b></div>
289
- <code>/linear_1.5_-30/sample.jpg</code>
284
+ <td valign="top" align="left" width="50%">
285
+ <div align="center"><img src="./assets/operations/linear.jpg?v=04090af0" width="320" alt="/linear_1.5_-30/colour.jpg"></div>
286
+ <div><b><code>linear</code></b></div>
287
+ <code>/linear_1.5_-30/colour.jpg</code>
290
288
  <br>
291
- Levels adjustment applying <code>a * input + b</code>, as <code>{a}_{b}</code>. <code>a</code> is the multiplier (defaults to <code>1</code>), <code>b</code> the offset (defaults to <code>0</code>). (<a href="https://sharp.pixelplumbing.com/api-operation#linear">docs</a>)
289
+ Levels adjustment applying <code>a * input + b</code>, as <code>{a}_{b}</code>. <code>a</code> is the multiplier (defaults to <code>1</code>) and <code>b</code> the offset (defaults to <code>0</code>), both numbers. (<a href="https://sharp.pixelplumbing.com/api-operation#linear">docs</a>)
292
290
  </td>
293
291
  </tr>
294
292
  <tr>
295
- <td valign="top" width="50%">
296
- <div align="center"><img src="./assets/operations/tint.jpg" alt="/tint_00ff00/sample.jpg"></div>
297
- <div align="center"><b><code>tint</code></b></div>
298
- <code>/tint_00ff00/sample.jpg</code>
293
+ <td valign="top" align="left" width="50%">
294
+ <div align="center"><img src="./assets/operations/tint.jpg?v=7a727e4a" width="320" alt="/tint_00ff00/colour.jpg"></div>
295
+ <div><b><code>tint</code></b></div>
296
+ <code>/tint_00ff00/colour.jpg</code>
299
297
  <br>
300
- Tint colour. (<a href="https://sharp.pixelplumbing.com/api-colour#tint">docs</a>)
298
+ Tint the image, keeping its luminance. A hex (<code>f00</code>, <code>ff0000</code>) or named (<code>red</code>) colour. (<a href="https://sharp.pixelplumbing.com/api-colour#tint">docs</a>)
301
299
  </td>
302
- <td valign="top" width="50%">
303
- <div align="center"><img src="./assets/operations/grayscale.jpg" alt="/grayscale/sample.jpg"></div>
304
- <div align="center"><b><code>grayscale</code></b></div>
305
- <code>/grayscale/sample.jpg</code>
300
+ <td valign="top" align="left" width="50%">
301
+ <div align="center"><img src="./assets/operations/grayscale.jpg?v=8e4a38ec" width="320" alt="/grayscale/colour.jpg"></div>
302
+ <div><b><code>grayscale</code></b></div>
303
+ <code>/grayscale/colour.jpg</code>
306
304
  <br>
307
- Convert to 8-bit greyscale. (<a href="https://sharp.pixelplumbing.com/api-colour#grayscale">docs</a>)
305
+ Convert the image to 8-bit greyscale, mapping every pixel to its luminance. (<a href="https://sharp.pixelplumbing.com/api-colour#grayscale">docs</a>)
308
306
  </td>
309
307
  </tr>
310
308
  <tr>
311
- <td valign="top" width="50%">
312
- <div align="center"><img src="./assets/operations/modulate.jpg" alt="/modulate_1.5_2_90_10/sample.jpg"></div>
313
- <div align="center"><b><code>modulate</code></b></div>
314
- <code>/modulate_1.5_2_90_10/sample.jpg</code>
309
+ <td valign="top" align="left" width="50%">
310
+ <div align="center"><img src="./assets/operations/modulate.jpg?v=f1cce46c" width="320" alt="/modulate_1.5_2_90_10/colour.jpg"></div>
311
+ <div><b><code>modulate</code></b></div>
312
+ <code>/modulate_1.5_2_90_10/colour.jpg</code>
315
313
  <br>
316
- Transforms the image using <code>{brightness}_{saturation}_{hue}_{lightness}</code>. <code>brightness</code> and <code>saturation</code> are numbers <code>&gt;= 0</code>, <code>hue</code> is an integer in degrees. Each is also available on its own (below), which is easier to read when only one is needed. (<a href="https://sharp.pixelplumbing.com/api-operation#modulate">docs</a>)
314
+ Transform the image, as <code>{brightness}_{saturation}_{hue}_{lightness}</code>. <code>brightness</code> and <code>saturation</code> are numbers <code>&gt;= 0</code>, <code>hue</code> an integer in degrees. Each is also available on its own (below). (<a href="https://sharp.pixelplumbing.com/api-operation#modulate">docs</a>)
317
315
  </td>
318
- <td valign="top" width="50%">
319
- <div align="center"><img src="./assets/operations/brightness.jpg" alt="/brightness_1.5/sample.jpg"></div>
320
- <div align="center"><b><code>brightness</code></b></div>
321
- <code>/brightness_1.5/sample.jpg</code>
316
+ <td valign="top" align="left" width="50%">
317
+ <div align="center"><img src="./assets/operations/brightness.jpg?v=c79f82bb" width="320" alt="/brightness_1.5/colour.jpg"></div>
318
+ <div><b><code>brightness</code></b></div>
319
+ <code>/brightness_1.5/colour.jpg</code>
322
320
  <br>
323
- Brightness multiplier, a number <code>&gt;= 0</code>. Required. (<a href="https://sharp.pixelplumbing.com/api-operation#modulate">docs</a>)
321
+ Brightness multiplier, a number <code>&gt;= 0</code> (<code>1</code> leaves the image unchanged). Required. (<a href="https://sharp.pixelplumbing.com/api-operation#modulate">docs</a>)
324
322
  </td>
325
323
  </tr>
326
324
  <tr>
327
- <td valign="top" width="50%">
328
- <div align="center"><img src="./assets/operations/saturation.jpg" alt="/saturation_0.3/sample.jpg"></div>
329
- <div align="center"><b><code>saturation</code></b></div>
330
- <code>/saturation_0.3/sample.jpg</code>
325
+ <td valign="top" align="left" width="50%">
326
+ <div align="center"><img src="./assets/operations/saturation.jpg?v=9a395c34" width="320" alt="/saturation_0.3/colour.jpg"></div>
327
+ <div><b><code>saturation</code></b></div>
328
+ <code>/saturation_0.3/colour.jpg</code>
331
329
  <br>
332
- Saturation multiplier, a number <code>&gt;= 0</code>. Required. (<a href="https://sharp.pixelplumbing.com/api-operation#modulate">docs</a>)
330
+ Saturation multiplier, a number <code>&gt;= 0</code> (<code>0</code> is greyscale). Required. (<a href="https://sharp.pixelplumbing.com/api-operation#modulate">docs</a>)
333
331
  </td>
334
- <td valign="top" width="50%">
335
- <div align="center"><img src="./assets/operations/hue.jpg" alt="/hue_90/sample.jpg"></div>
336
- <div align="center"><b><code>hue</code></b></div>
337
- <code>/hue_90/sample.jpg</code>
332
+ <td valign="top" align="left" width="50%">
333
+ <div align="center"><img src="./assets/operations/hue.jpg?v=8dff0588" width="320" alt="/hue_90/colour.jpg"></div>
334
+ <div><b><code>hue</code></b></div>
335
+ <code>/hue_90/colour.jpg</code>
338
336
  <br>
339
- Hue rotation, an integer in degrees. Required. (<a href="https://sharp.pixelplumbing.com/api-operation#modulate">docs</a>)
337
+ Hue rotation, an integer in degrees that wraps around at <code>360</code>. Required. (<a href="https://sharp.pixelplumbing.com/api-operation#modulate">docs</a>)
340
338
  </td>
341
339
  </tr>
342
340
  <tr>
343
- <td valign="top" width="50%">
344
- <div align="center"><img src="./assets/operations/lightness.jpg" alt="/lightness_30/sample.jpg"></div>
345
- <div align="center"><b><code>lightness</code></b></div>
346
- <code>/lightness_30/sample.jpg</code>
341
+ <td valign="top" align="left" width="50%">
342
+ <div align="center"><img src="./assets/operations/lightness.jpg?v=fe72d3a8" width="320" alt="/lightness_30/colour.jpg"></div>
343
+ <div><b><code>lightness</code></b></div>
344
+ <code>/lightness_30/colour.jpg</code>
347
345
  <br>
348
- Lightness addend, a number. Required. (<a href="https://sharp.pixelplumbing.com/api-operation#modulate">docs</a>)
346
+ Lightness addend, a number added to the lightness of every pixel. Required. (<a href="https://sharp.pixelplumbing.com/api-operation#modulate">docs</a>)
349
347
  </td>
350
- <td valign="top" width="50%">
351
- <div align="center"><img src="./assets/operations/opacity.webp" alt="/opacity_0.5,f_webp/sample.jpg"></div>
352
- <div align="center"><b><code>opacity</code></b></div>
353
- <code>/opacity_0.5,f_webp/sample.jpg</code>
348
+ <td valign="top" align="left" width="50%">
349
+ <div align="center"><img src="./assets/operations/opacity.webp?v=75d9647c" width="320" alt="/opacity_0.5,f_webp/alpha.png"></div>
350
+ <div><b><code>opacity</code></b></div>
351
+ <code>/opacity_0.5,f_webp/alpha.png</code>
354
352
  <br>
355
- Opacity, a number between <code>0</code> and <code>1</code>. Required. The image is made transparent, so the output format needs an alpha channel (<code>png</code>, <code>webp</code>, <code>avif</code>). Set the <code>background</code> / <code>b</code> colour to blend into it instead and keep the output opaque, e.g. <code>opacity_0.5,b_fff,f_jpeg</code>.
353
+ Opacity, a number between <code>0</code> and <code>1</code>. Required. The image is made transparent, so the output format needs an alpha channel (<code>png</code>, <code>webp</code>, <code>avif</code>), or set the <code>background</code> / <code>b</code> colour to blend into it instead, e.g. <code>opacity_0.5,b_fff,f_jpeg</code>.
356
354
  </td>
357
355
  </tr>
358
356
  <tr>
359
- <td valign="top" width="50%">
360
- <div align="center"><img src="./assets/operations/animated.gif" alt="/a,w_160/sample.gif"></div>
361
- <div align="center"><b><code>animated</code> / <code>a</code></b></div>
362
- <code>/a,w_160/sample.gif</code>
357
+ <td valign="top" align="left" width="50%">
358
+ <div align="center"><img src="./assets/operations/animated.gif?v=e51060bb" width="160" alt="/a,w_160/animated.gif"></div>
359
+ <div><b><code>animated</code> / <code>a</code></b></div>
360
+ <code>/a,w_160/animated.gif</code>
363
361
  <br>
364
- Process every frame of an animated image. Experimental.
362
+ Process every frame of an animated image instead of the first one. Experimental.
365
363
  </td>
366
- <td valign="top" width="50%">
364
+ <td valign="top" align="left" width="50%">
367
365
  <div align="center"><em>No sample: the sample source has no uniform border to trim.</em></div>
368
- <div align="center"><b><code>trim</code></b></div>
369
- <code>/trim_30/sample.jpg</code>
366
+ <div><b><code>trim</code></b></div>
367
+ <code>/trim_30/photo.jpg</code>
370
368
  <br>
371
- Trim pixels from the edges that are within the threshold of the top-left pixel colour. Threshold is a number <code>&gt;= 0</code> (defaults to <code>10</code>). (<a href="https://sharp.pixelplumbing.com/api-resize#trim">docs</a>)
369
+ Trim edge pixels that are within the threshold of the top-left pixel colour. Threshold is a number <code>&gt;= 0</code> (defaults to <code>10</code>). (<a href="https://sharp.pixelplumbing.com/api-resize#trim">docs</a>)
372
370
  </td>
373
371
  </tr>
374
372
  <tr>
375
- <td valign="top" width="50%">
373
+ <td valign="top" align="left" width="50%">
376
374
  <div align="center"><em>No sample: the sample source carries no EXIF <code>Orientation</code> tag.</em></div>
377
- <div align="center"><b><code>autoorient</code></b></div>
378
- <code>/autoorient/sample.jpg</code>
375
+ <div><b><code>autoorient</code></b></div>
376
+ <code>/autoorient/photo.jpg</code>
379
377
  <br>
380
- Rotate and flip the image based on its EXIF <code>Orientation</code> tag, then remove the tag. (<a href="https://sharp.pixelplumbing.com/api-operation#autoorient">docs</a>)
378
+ Rotate and flip the image according to its EXIF <code>Orientation</code> tag, then remove the tag. Also applied by <code>rotate</code> without an angle. (<a href="https://sharp.pixelplumbing.com/api-operation#autoorient">docs</a>)
381
379
  </td>
382
- <td valign="top" width="50%"></td>
380
+ <td valign="top" align="left" width="50%"></td>
383
381
  </tr>
384
382
  </table>
385
383
 
@@ -482,9 +480,28 @@ Every option can also be set universally with an `IPX_*` environment variable, w
482
480
  | -------------------- | -------------------------- | ------- | --------------------------------------------------------- |
483
481
  | `alias` | `IPX_ALIAS` | `{}` | Map URL prefixes to other prefixes or remote origins. |
484
482
  | `maxOutputDimension` | `IPX_MAX_OUTPUT_DIMENSION` | `8192` | Maximum width and height (in pixels) of the output image. |
483
+ | `allowedModifiers` | `IPX_ALLOWED_MODIFIERS` | (all) | Modifiers that requests are allowed to use. |
485
484
 
486
485
  Requested `width`, `height` and `resize` dimensions are clamped to `maxOutputDimension`, preserving the requested aspect ratio, and `extend` edges are clamped so the extended canvas stays within it. This bounds how much memory a single request can allocate: sharp only limits the _input_ size, so without it `/enlarge,s_20000x20000/image.jpg` (or `/extend_10000_10000_10000_10000/image.jpg`) allocates gigabytes from a small source image. Set to `false` to disable, which is only safe when modifiers come from a trusted source.
487
486
 
487
+ `allowedModifiers` restricts a public endpoint to the modifiers a site actually uses, for example `allowedModifiers: ["width", "height", "format", "quality"]` or `IPX_ALLOWED_MODIFIERS=width,height,format,quality`. Aliases follow their modifier (allowing `width` also allows `w`). Any other modifier, including an unknown one, is rejected with a `400` before the source is fetched, so expensive operations (`blur`, `median`, ...) cannot be triggered. Only modifier names are checked, not their values: allowing `format` allows every supported output format, including the slow-to-encode `avif`. The same image also stays reachable under many URLs (`?cb=1`, `w_10` vs `w_1e1`, ...), so this does not prevent busting a CDN cache.
488
+
489
+ ### Server (`createIPXFetchHandler`, `createIPXNodeHandler`, `serveIPX`)
490
+
491
+ | Option | Environment variable | Default | Description |
492
+ | ------------- | -------------------- | ---------------------------------------------------- | ------------------------------------------------------- |
493
+ | `autoFormats` | `IPX_AUTO_FORMATS` | `avif`, `webp`, `jpeg`, `png`, `tiff`, `heif`, `gif` | Formats `f_auto` can pick from, in order of preference. |
494
+
495
+ `f_auto` serves the format that the client's `accept` header lists with the highest q-value, and equal q-values go to the earlier entry in `autoFormats`. Wildcards such as `image/*` and `*/*` do not match a format, so browsers only negotiate the formats they list explicitly (in practice `avif`, `webp` and `png`). When nothing matches, `jpeg` is served, even if it is not in the list. Animated images only consider `webp` and `gif`, and fall back to `gif`.
496
+
497
+ AVIF compresses best but is much slower to encode, so leave it out when encoding time matters more than size:
498
+
499
+ ```ts
500
+ createIPXFetchHandler(ipx, { autoFormats: ["webp", "jpeg"] });
501
+ ```
502
+
503
+ With the CLI: `IPX_AUTO_FORMATS=webp,jpeg ipx serve`. Unknown formats throw when the handler is created.
504
+
488
505
  ### Filesystem source (`ipxFSStorage`)
489
506
 
490
507
  Enabled by default with the CLI only.
@@ -499,13 +516,13 @@ Enabled by default with the CLI only.
499
516
 
500
517
  Enabled by default with the CLI only.
501
518
 
502
- | Option | Environment variable | Default | Description |
503
- | ----------------- | ---------------------------- | ------- | --------------------------------------------------------------- |
504
- | `domains` | `IPX_HTTP_DOMAINS` | `[]` | Allowlist of hostnames images can be fetched from. |
505
- | `maxAge` | `IPX_HTTP_MAX_AGE` | `300` | |
506
- | `fetchOptions` | `IPX_HTTP_FETCH_OPTIONS` | `{}` | Passed to `fetch()`. |
507
- | `allowAllDomains` | `IPX_HTTP_ALLOW_ALL_DOMAINS` | `false` | Disables the allowlist. Unsafe on a public server. |
508
- | `blockPrivateIPs` | `IPX_HTTP_BLOCK_PRIVATE_IPS` | `false` | Rejects hosts that are, or resolve to, a non-public IP address. |
519
+ | Option | Environment variable | Default | Description |
520
+ | ----------------- | ---------------------------- | ------- | --------------------------------------------------------------------------------------------- |
521
+ | `domains` | `IPX_HTTP_DOMAINS` | `[]` | Allowlist of hostnames images can be fetched from. |
522
+ | `maxAge` | `IPX_HTTP_MAX_AGE` | `300` | `cache-control` max-age, in seconds, when upstream sends none. |
523
+ | `fetchOptions` | `IPX_HTTP_FETCH_OPTIONS` | `{}` | Passed to `fetch()`. |
524
+ | `allowAllDomains` | `IPX_HTTP_ALLOW_ALL_DOMAINS` | `false` | Disables the allowlist. Unsafe on a public server. |
525
+ | `blockPrivateIPs` | `IPX_HTTP_BLOCK_PRIVATE_IPS` | `false` | Rejects hosts that are, or resolve to, a non-public IP address (best effort, see note below). |
509
526
 
510
527
  Only `http:` and `https:` URLs are allowed (anything else is rejected with `403 IPX_FORBIDDEN_PROTOCOL`), and redirects are followed **only within the allowlist**, up to 3 hops: each redirect target is re-validated and a redirect to a host that is not listed is rejected with `403 IPX_FORBIDDEN_HOST` (`502 IPX_TOO_MANY_REDIRECTS` beyond 3 hops). Previously redirects were followed blindly, which let an allowlisted host with an open redirect bounce IPX to internal addresses such as the cloud metadata service (SSRF). If an allowlisted host redirects to a CDN, add the CDN hostname to the allowlist as well. Redirect re-validation is skipped when `allowAllDomains` is enabled without `blockPrivateIPs` (nothing to validate) or when `redirect` is explicitly set in `fetchOptions`.
511
528
 
@@ -521,7 +538,10 @@ The host of the requested URL **and of every redirect hop** must be a public add
521
538
 
522
539
  It is **off by default**: many deployments legitimately fetch from in-cluster origins (internal object storage, a sidecar, `localhost` in development), and enabling it by default would break them.
523
540
 
524
- Two limits to be aware of. It does not close the DNS-rebinding window: the name is resolved again when the socket is opened, so a record with a very short TTL can answer with a public address during validation and a private one during the fetch. Closing that requires pinning the validated address on the connection itself, which the plain `fetch` used here does not expose. And it is not a substitute for the allowlist — a public address that happens to be reachable is still fetched.
541
+ > [!WARNING]
542
+ > `blockPrivateIPs` is best effort and does **not** protect against DNS rebinding. The hostname is resolved once to validate it and again when `fetch` opens the socket, so a DNS record the attacker controls (with a very short TTL) can answer with a public address during validation and a private one during the fetch. This matters when the attacker can choose the hostname: with `allowAllDomains`, or an allowlisted domain whose DNS you do not control. If IPX must not reach internal services, enforce it at the network level: run it behind an egress proxy, or a firewall / network policy that blocks private ranges and the cloud metadata service (on AWS, also require IMDSv2).
543
+
544
+ It is also not a substitute for the allowlist — a public address that happens to be reachable is still fetched.
525
545
 
526
546
  ## SVG Images
527
547
 
@@ -628,13 +648,6 @@ The parser may be async, and can throw an `HTTPError` (re-exported from `ipx`) t
628
648
 
629
649
  Returned values are escaped by IPX, so custom parsers don't need to do it themselves. Note this is not an access check — exactly as with the default URL style, what the resulting id is allowed to resolve to is enforced by the storage layer (`ipxFSStorage`'s directory boundary, `ipxHttpStorage`'s domain allowlist).
630
650
 
631
- ## Migration from v3 to v4
632
-
633
- - The server creation APIs have changed. See the [Programmatic API](#programmatic-api) section for examples.
634
- - The JSON error format has changed from `{ error: string }` to `{ status, statusText, message }`.
635
- - The `svgo` option is now `svg.optimize`. SVG images are always sanitized, even when optimization is disabled.
636
- - SVG optimization now applies SVGO's `preset-default` unless custom `plugins` are configured (previously only the configured plugins ran), so SVG output is restructured more than before. See the [SVG Images](#svg-images) section.
637
-
638
651
  ## License
639
652
 
640
653
  [MIT](./LICENSE)