ipx 4.0.0-alpha.2 → 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.
@@ -5,14 +5,6 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __esmMin = (fn, res, err) => () => {
9
- if (err) throw err[0];
10
- try {
11
- return fn && (res = fn(fn = 0)), res;
12
- } catch (e) {
13
- throw err = [e], e;
14
- }
15
- };
16
8
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
17
9
  var __exportAll = (all, no_symbols) => {
18
10
  let target = {};
@@ -33,10 +25,9 @@ var __copyProps = (to, from, except, desc) => {
33
25
  }
34
26
  return to;
35
27
  };
36
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
28
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
37
29
  value: mod,
38
30
  enumerable: true
39
31
  }) : target, mod));
40
- var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
41
32
  var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
42
- export { __commonJSMin, __esmMin, __exportAll, __require, __toCommonJS, __toESM };
33
+ export { __commonJSMin, __exportAll, __require, __toESM };
package/dist/cli.d.mts CHANGED
@@ -1 +1 @@
1
- export { };
1
+ export {}
package/dist/cli.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import { createIPX, ipxFSStorage, ipxHttpStorage, serveIPX } from "./_chunks/node-fs.mjs";
3
3
  import { parseArgs } from "node:util";
4
4
  var name = "ipx";
5
- var version = "4.0.0-alpha.1";
5
+ var version = "4.0.0";
6
6
  const { positionals, values } = parseArgs({
7
7
  allowPositionals: true,
8
8
  options: {
@@ -39,10 +39,11 @@ switch (command) {
39
39
  process.exit(1);
40
40
  }
41
41
  async function runServe(args) {
42
- await serveIPX(createIPX({
42
+ const ipx = createIPX({
43
43
  storage: ipxFSStorage({ dir: args.dir ?? process.env.IPX_FS_DIR ?? process.cwd() }),
44
44
  httpStorage: ipxHttpStorage({ domains: args.domains ?? process.env.IPX_HTTP_DOMAINS })
45
- }), {
45
+ });
46
+ await serveIPX(ipx, {
46
47
  port: Number(args.port ?? process.env.PORT ?? 3e3),
47
48
  hostname: args.host ?? process.env.HOST ?? "0.0.0.0"
48
49
  }).ready();
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ImageMeta } from "./_chunks/libs/image-meta.mjs";
2
+ import { HTTPError } from "./_chunks/libs/h3.mjs";
2
3
  import { NodeHttpHandler, Server, ServerOptions } from "srvx";
3
4
  import { Color, KernelEnum, Sharp, SharpOptions } from "sharp";
4
5
  import { Config } from "svgo";
@@ -34,6 +35,30 @@ interface HandlerContext {
34
35
  * @optional
35
36
  */
36
37
  kernel?: keyof KernelEnum;
38
+ /**
39
+ * The maximum width and height of the output image, or `false` when
40
+ * unlimited. Set from the `maxOutputDimension` IPX option.
41
+ * @optional
42
+ */
43
+ maxOutputDimension?: number | false;
44
+ /**
45
+ * Dimensions the image is expected to have once the resize modifiers
46
+ * (`resize`, `width`, `height`) have been applied, recorded by those
47
+ * handlers after their own clamping.
48
+ *
49
+ * libvips resizes before it extends, whatever order the modifiers appear in
50
+ * the URL, so `extend` budgets its edges against these rather than against
51
+ * {@link meta}: budgeting against the source lets the two clamps stack and
52
+ * the canvas end up at twice `maxOutputDimension` on each axis.
53
+ *
54
+ * Undefined when no resize modifier was applied (or when nothing could be
55
+ * projected from it), in which case `extend` falls back to {@link meta}.
56
+ * @optional
57
+ */
58
+ outputDimensions?: {
59
+ width?: number;
60
+ height?: number;
61
+ };
37
62
  /**
38
63
  * Metadata about the image being processed.
39
64
  */
@@ -106,6 +131,7 @@ type IPXSourceMeta = {
106
131
  */
107
132
  maxAge?: number;
108
133
  };
134
+ type FlagModifier = true | "true" | "";
109
135
  type FormatModifier = "jpeg" | "jpg" | "png" | "webp" | "avif" | "gif" | "heif" | "tiff" | "auto" | (string & {});
110
136
  interface IPXModifiers {
111
137
  format: FormatModifier;
@@ -119,28 +145,39 @@ interface IPXModifiers {
119
145
  b: string;
120
146
  position: string;
121
147
  pos: string;
122
- enlarge: true | "true";
148
+ enlarge: FlagModifier | false | "false";
123
149
  kernel: "nearest" | "cubic" | "mitchell" | "lanczos2" | "lanczos3" | (string & {});
124
150
  trim: number | string;
125
151
  extend: string;
126
152
  extract: string;
127
153
  crop: string;
128
154
  rotate: number | string;
129
- flip: true | "true";
130
- flop: true | "true";
155
+ autoorient: FlagModifier;
156
+ flip: FlagModifier;
157
+ flop: FlagModifier;
131
158
  sharpen: number | string;
132
159
  median: number | string;
133
160
  blur: number | string;
134
- flatten: true | "true";
161
+ dilate: number | string;
162
+ erode: number | string;
163
+ clahe: number | string;
164
+ flatten: FlagModifier;
165
+ unflatten: FlagModifier;
135
166
  gamma: string;
136
- negate: true | "true";
137
- normalize: true | "true";
167
+ negate: FlagModifier | string;
168
+ normalize: FlagModifier | string;
138
169
  threshold: number | string;
170
+ linear: string;
139
171
  modulate: string;
172
+ brightness: number | string;
173
+ saturation: number | string;
174
+ hue: number | string;
175
+ lightness: number | string;
176
+ opacity: number | string;
140
177
  tint: number | string;
141
- grayscale: true | "true";
142
- animated: true | "true";
143
- a: true | "true";
178
+ grayscale: FlagModifier;
179
+ animated: FlagModifier;
180
+ a: FlagModifier;
144
181
  width: string | number;
145
182
  w: string | number;
146
183
  height: string | number;
@@ -177,6 +214,45 @@ type IPXOptions = {
177
214
  * @optional
178
215
  */
179
216
  maxAge?: number;
217
+ /**
218
+ * Maximum width and height (in pixels) of the output image.
219
+ *
220
+ * Modifiers are user input and sharp's `limitInputPixels` only bounds the
221
+ * *input*, so without this limit a request such as
222
+ * `/enlarge,s_20000x20000/img.jpg` or `/extend_10000_10000_10000_10000/img.jpg`
223
+ * makes sharp allocate a multi-gigabyte output buffer from a tiny source
224
+ * image, which is an easy way to exhaust the server memory.
225
+ *
226
+ * Requested `width`, `height`, `resize` dimensions are clamped to this value
227
+ * (preserving the requested aspect ratio) and `extend` edges are clamped so
228
+ * that the extended canvas stays within it.
229
+ *
230
+ * Set to `false` to disable the limit (only safe when modifiers come from a
231
+ * trusted source).
232
+ *
233
+ * @default 8192
234
+ * @optional
235
+ */
236
+ maxOutputDimension?: number | false;
237
+ /**
238
+ * Modifiers that requests are allowed to use. Any other modifier (including
239
+ * an unknown one) is rejected with a `400` before the source is fetched.
240
+ *
241
+ * Every modifier is allowed by default. Restricting them to the ones a site
242
+ * actually uses (for example `["width", "height", "format", "quality"]`)
243
+ * shrinks the attack surface of the public endpoint: expensive operations
244
+ * such as `blur` or `median` cannot be triggered.
245
+ *
246
+ * Aliases follow their modifier: allowing `width` also allows `w`.
247
+ *
248
+ * Only modifier names are checked, not their values: allowing `format`
249
+ * allows every supported output format, including the slow-to-encode `avif`.
250
+ * The same image also stays reachable under many URLs (`?cb=1`, `w_10` vs
251
+ * `w_1e1`, ...), so this does not prevent busting a CDN cache.
252
+ *
253
+ * @optional
254
+ */
255
+ allowedModifiers?: (keyof IPXModifiers)[];
180
256
  /**
181
257
  * A mapping of URL aliases to their corresponding URLs, used to simplify resource identifiers.
182
258
  * @optional
@@ -197,10 +273,28 @@ type IPXOptions = {
197
273
  */
198
274
  httpStorage?: IPXStorage;
199
275
  /**
200
- * Configuration for the SVGO library used when processing SVG images.
276
+ * Options for SVG images, which are processed by SVGO instead of Sharp.
201
277
  * @optional
202
278
  */
203
- svgo?: false | Config;
279
+ svg?: {
280
+ /**
281
+ * Configuration for the SVGO library used to optimize SVG images.
282
+ *
283
+ * SVGO's `preset-default` is applied unless custom `plugins` are configured.
284
+ * Set to `false` to disable optimization. Sanitization is applied either way.
285
+ * @optional
286
+ */
287
+ optimize?: false | Config;
288
+ /**
289
+ * Disable SVG sanitization.
290
+ *
291
+ * Sanitized SVG images cannot execute scripts. Only disable this if all sources
292
+ * are fully trusted, as it allows serving SVG images with XSS payloads.
293
+ * @default false
294
+ * @optional
295
+ */
296
+ unsafeSkipSanitize?: boolean;
297
+ };
204
298
  };
205
299
  /**
206
300
  * Creates an IPX image processing instance with the specified options.
@@ -208,24 +302,107 @@ type IPXOptions = {
208
302
  * @returns {IPX} An IPX processing function configured with the given options. See {@link IPX}.
209
303
  * @throws {Error} If critical options such as storage are missing or incorrectly configured.
210
304
  */
211
- declare function createIPX(userOptions: IPXOptions): IPX;
305
+ export declare function createIPX(userOptions: IPXOptions): IPX;
212
306
  type FetchHandler = (request: Request | string | URL) => Response | Promise<Response>;
213
- declare function createIPXFetchHandler(ipx: IPX): FetchHandler;
214
- declare function createIPXNodeHandler(ipx: IPX): NodeHttpHandler;
215
- declare function serveIPX(ipx: IPX, opts?: Omit<ServerOptions, "fetch">): Server;
307
+ interface IPXHandlerOptions {
308
+ /**
309
+ * Custom URL parser to extract the resource id and modifiers from the request URL.
310
+ * Can be async.
311
+ *
312
+ * Receives the raw (absolute, still percent-encoded) request URL, so parsers can
313
+ * decode it themselves without going through h3's normalization.
314
+ *
315
+ * Defaults to {@link parseIPXURL} which handles URLs in the form `/<modifiers>/<id>`.
316
+ *
317
+ * Returned values are escaped (control characters) by the handler, so custom parsers
318
+ * do not need to escape them. This is not an access check: exactly as with the default
319
+ * parser, what the resulting `id` is allowed to resolve to is enforced by the storage
320
+ * layer. Throw an `HTTPError` to reject a request with a specific status code.
321
+ *
322
+ * @optional
323
+ */
324
+ parseURL?: IPXURLParser;
325
+ /**
326
+ * Output formats `f_auto` can pick from, in order of preference.
327
+ *
328
+ * Useful to leave out formats that are slow to encode, such as `avif`.
329
+ *
330
+ * The format the client's `Accept` header lists with the highest q-value wins,
331
+ * and equal q-values go to the earlier entry. Wildcards such as `image/*` do
332
+ * not match a format, so browsers only negotiate the formats they list
333
+ * explicitly (in practice `avif`, `webp` and `png`).
334
+ *
335
+ * Animated images only consider `webp` and `gif` from this list. When nothing
336
+ * matches, `jpeg` (or `gif` for animated images) is used.
337
+ *
338
+ * Unknown formats throw when the handler is created.
339
+ *
340
+ * Can also be set with the `IPX_AUTO_FORMATS` environment variable (JSON array
341
+ * or comma separated list).
342
+ *
343
+ * @default ["avif", "webp", "jpeg", "png", "tiff", "heif", "gif"]
344
+ */
345
+ autoFormats?: IPXAutoFormat[];
346
+ }
347
+ type IPXAutoFormat = "avif" | "webp" | "jpeg" | "jpg" | "png" | "tiff" | "heif" | "heic" | "gif";
348
+ export declare function createIPXFetchHandler(ipx: IPX, opts?: IPXHandlerOptions): FetchHandler;
349
+ export declare function createIPXNodeHandler(ipx: IPX, opts?: IPXHandlerOptions): NodeHttpHandler;
350
+ export declare function serveIPX(ipx: IPX, opts?: Omit<ServerOptions, "fetch"> & IPXHandlerOptions): Server;
351
+ interface IPXParsedURL {
352
+ /**
353
+ * The identifier of the source image (path or URL, depending on the storage).
354
+ */
355
+ id: string;
356
+ /**
357
+ * Modifiers to apply, keyed by modifier name. See {@link IPXModifiers}.
358
+ *
359
+ * Values are coerced to strings. Use `""` (or `undefined`) for valueless
360
+ * modifiers such as `grayscale`.
361
+ */
362
+ modifiers: Record<string, string | number | boolean | undefined>;
363
+ }
364
+ type IPXURLParser = (url: string) => IPXParsedURL | Promise<IPXParsedURL>;
365
+ /**
366
+ * Default IPX URL parser, handling URLs in the form `/<modifiers>/<id>`.
367
+ *
368
+ * Use `_` as the modifiers segment to apply none (`/_/image.png`).
369
+ *
370
+ * @param {string} url - The raw (absolute) request URL.
371
+ * @returns {IPXParsedURL} The parsed resource id and modifiers. See {@link IPXParsedURL}.
372
+ * @throws {HTTPError} If the modifiers segment is missing.
373
+ */
374
+ export declare function parseIPXURL(url: string): IPXParsedURL;
216
375
  type HTTPStorageOptions = {
217
376
  /**
218
377
  * Custom options for fetch operations, such as headers or method overrides.
378
+ *
379
+ * Redirects are followed manually (one hop at a time) so that each redirect target
380
+ * can be re-validated against the {@link HTTPStorageOptions.domains} allowlist
381
+ * (and {@link HTTPStorageOptions.blockPrivateIPs} when enabled).
382
+ * Explicitly setting `redirect` here opts out of this protection: the value is passed
383
+ * to `fetch` as-is and redirect targets are **not** validated (a redirect to an
384
+ * internal address is then possible: SSRF).
219
385
  * @optional
220
386
  */
221
387
  fetchOptions?: RequestInit;
222
388
  /**
223
- * Default maximum age (in seconds) for cache control. If not specified, defaults to the environment setting or 300 seconds.
389
+ * Fallback `cache-control` max-age (in seconds), used when the upstream response has no
390
+ * `max-age` directive or {@link HTTPStorageOptions.ignoreCacheControl} is set. If not
391
+ * specified, defaults to `IPX_HTTP_MAX_AGE` or 300 seconds.
392
+ *
393
+ * `0` is kept rather than replaced by the default: caches may still store the response but
394
+ * must revalidate it on every request. It does not override an upstream `max-age` unless
395
+ * `ignoreCacheControl` is set. Invalid values (negative, `NaN`, non-numeric) count as unset.
224
396
  * @optional
225
397
  */
226
398
  maxAge?: number;
227
399
  /**
228
400
  * Whitelist of domains from which resource fetching is allowed. Can be a single string or an array of strings.
401
+ *
402
+ * Only `http:` and `https:` URLs are allowed. Redirects are followed only within this
403
+ * allowlist (up to 3 hops): a redirect to a host that is not listed is rejected with
404
+ * `403 IPX_FORBIDDEN_HOST`, so an allowlisted host cannot bounce IPX to an internal
405
+ * address. Hosts that redirect to a CDN must have the CDN hostname listed here too.
229
406
  * @optional
230
407
  */
231
408
  domains?: string | string[];
@@ -239,6 +416,28 @@ type HTTPStorageOptions = {
239
416
  * @optional
240
417
  */
241
418
  ignoreCacheControl?: boolean;
419
+ /**
420
+ * If set to true, reject URLs whose host is (or resolves to) a non-public IP address
421
+ * with `403 IPX_FORBIDDEN_IP`. Checked for the requested URL and for every redirect hop.
422
+ *
423
+ * This is **defense in depth**, not the primary access control: the
424
+ * {@link HTTPStorageOptions.domains} allowlist is. It only helps when an allowlisted
425
+ * (or, with `allowAllDomains`, arbitrary) hostname points at loopback, link-local
426
+ * (`169.254.169.254`, the cloud metadata service), RFC1918, CGNAT, unique-local or
427
+ * otherwise non-public space.
428
+ *
429
+ * Best effort: it does not protect against DNS rebinding. The hostname is validated, then
430
+ * resolved again when the socket is opened, so an attacker-controlled record with a short
431
+ * TTL can answer with a public address first and a private one second. When IPX must not
432
+ * reach internal services, enforce that at the network level (egress proxy, firewall or
433
+ * network policy) instead of relying on this option alone.
434
+ *
435
+ * Disabled by default because many legitimate deployments fetch from in-cluster origins
436
+ * (internal object storage, sidecars, `localhost` during development), which this blocks.
437
+ * @optional
438
+ * @default false
439
+ */
440
+ blockPrivateIPs?: boolean;
242
441
  };
243
442
  /**
244
443
  * Creates an HTTP storage handler for IPX that fetches image data from external URLs.
@@ -248,7 +447,7 @@ type HTTPStorageOptions = {
248
447
  * @returns {IPXStorage} An IPXStorage interface implementation for retrieving images over HTTP. See {@link IPXStorage}.
249
448
  * @throws {H3Error} If validation of the requested URL fails due to a missing hostname or denied host access. See {@link H3Error}.
250
449
  */
251
- declare function ipxHttpStorage(_options?: HTTPStorageOptions): IPXStorage;
450
+ export declare function ipxHttpStorage(_options?: HTTPStorageOptions): IPXStorage;
252
451
  type NodeFSSOptions = {
253
452
  /**
254
453
  * The directory or list of directories from which to serve files. If not specified, the current directory is used by default.
@@ -256,10 +455,32 @@ type NodeFSSOptions = {
256
455
  */
257
456
  dir?: string | string[];
258
457
  /**
259
- * The directory or list of directories from which to serve files. If not specified, the current directory is used by default.
458
+ * `max-age`, in seconds, reported for files served from this storage. Used for the
459
+ * `cache-control` header. Falls back to the IPX-wide `maxAge` (default 60) when unset.
460
+ *
461
+ * `0` is meaningful and is passed through as-is — it disables caching rather than
462
+ * falling back to the default.
260
463
  * @optional
261
464
  */
262
465
  maxAge?: number;
466
+ /**
467
+ * If set to true, a symlink inside {@link NodeFSSOptions.dir} may resolve to a file outside
468
+ * of it. By default such files are rejected with `403 IPX_FORBIDDEN_SYMLINK`, since the
469
+ * lexical `../` check alone says nothing about where a path physically ends up.
470
+ *
471
+ * Symlinks that stay inside `dir` are always followed, whatever this is set to.
472
+ *
473
+ * This is **defense in depth**, not a fix for a remotely reachable hole: planting a symlink
474
+ * in the served directory already requires a symlink-create or arbitrary-write primitive
475
+ * there, and the served filesystem is assumed to be trusted. Comparable static file servers
476
+ * (`express.static`, nginx with `disable_symlinks off`) follow symlinks by default too.
477
+ *
478
+ * Enable it for the deployments where symlinking assets into the served directory is
479
+ * deliberate — monorepo `public/` layouts, CI artifact linking, Docker layer tricks.
480
+ * @optional
481
+ * @default false
482
+ */
483
+ allowSymlinksOutsideDir?: boolean;
263
484
  };
264
485
  /**
265
486
  * Creates a file system storage handler for IPX that allows images to be served from local directories specified in the options.
@@ -269,7 +490,7 @@ type NodeFSSOptions = {
269
490
  * @returns {IPXStorage} An implementation of the IPXStorage interface for accessing images stored on the local file system. See {@link IPXStorage}.
270
491
  * @throws {H3Error} If there is a problem accessing the file system module or resolving/reading files. See {@link H3Error}.
271
492
  */
272
- declare function ipxFSStorage(_options?: NodeFSSOptions): IPXStorage;
493
+ export declare function ipxFSStorage(_options?: NodeFSSOptions): IPXStorage;
273
494
  type UnstorageIPXStorageOptions = {
274
495
  /**
275
496
  * Optional prefix to be placed in front of each storage key, which can help to name or categorise stored items.
@@ -286,5 +507,5 @@ type UnstorageIPXStorageOptions = {
286
507
  * @returns {IPXStorage}. An IPXStorage compliant object that implements the necessary methods to interact with the provided unstorage driver or storage system. See {@link IPXStorage}.
287
508
  * @throws {H3Error} If there is a problem retrieving or converting the storage data, detailed error information is thrown. See {@link H3Error}.
288
509
  */
289
- declare function unstorageToIPXStorage(storage: Storage | Driver, _options?: UnstorageIPXStorageOptions | string): IPXStorage;
290
- export { type HTTPStorageOptions, type Handler, type HandlerContext, type IPX, type IPXModifiers, type IPXOptions, type IPXStorage, type IPXStorageMeta, type IPXStorageOptions, type NodeFSSOptions, type UnstorageIPXStorageOptions, createIPX, createIPXFetchHandler, createIPXNodeHandler, ipxFSStorage, ipxHttpStorage, serveIPX, unstorageToIPXStorage };
510
+ export declare function unstorageToIPXStorage(storage: Storage | Driver, _options?: UnstorageIPXStorageOptions | string): IPXStorage;
511
+ export { HTTPError, type HTTPStorageOptions, type Handler, type HandlerContext, type IPX, type IPXAutoFormat, type IPXHandlerOptions, type IPXModifiers, type IPXOptions, type IPXParsedURL, type IPXStorage, type IPXStorageMeta, type IPXStorageOptions, type IPXURLParser, type NodeFSSOptions, type UnstorageIPXStorageOptions };
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { HTTPError } from "./_chunks/libs/h3.mjs";
2
- import { createIPX, createIPXFetchHandler, createIPXNodeHandler, ipxFSStorage, ipxHttpStorage, serveIPX } from "./_chunks/node-fs.mjs";
2
+ import { createIPX, createIPXFetchHandler, createIPXNodeHandler, ipxFSStorage, ipxHttpStorage, parseIPXURL, serveIPX } from "./_chunks/node-fs.mjs";
3
3
  function unstorageToIPXStorage(storage, _options = {}) {
4
4
  const options = typeof _options === "string" ? { prefix: _options } : _options;
5
5
  const resolveKey = (id) => options.prefix ? `${options.prefix}:${id}` : id;
@@ -29,4 +29,4 @@ function unstorageToIPXStorage(storage, _options = {}) {
29
29
  }
30
30
  };
31
31
  }
32
- export { createIPX, createIPXFetchHandler, createIPXNodeHandler, ipxFSStorage, ipxHttpStorage, serveIPX, unstorageToIPXStorage };
32
+ export { HTTPError, createIPX, createIPXFetchHandler, createIPXNodeHandler, ipxFSStorage, ipxHttpStorage, parseIPXURL, serveIPX, unstorageToIPXStorage };
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "ipx",
3
- "version": "4.0.0-alpha.2",
3
+ "version": "4.0.0",
4
4
  "description": "High performance, secure and easy-to-use image optimizer.",
5
- "repository": "unjs/ipx",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "unjs/ipx"
8
+ },
6
9
  "license": "MIT",
7
10
  "sideEffects": false,
8
11
  "type": "module",
@@ -17,42 +20,30 @@
17
20
  "files": [
18
21
  "dist"
19
22
  ],
20
- "scripts": {
21
- "build": "obuild",
22
- "ipx": "node ./src/cli.ts",
23
- "lint": "eslint . && prettier -c src test",
24
- "lint:fix": "automd && eslint . --fix && prettier -w src test",
25
- "prepack": "pnpm build",
26
- "release": "pnpm test && pnpm build && changelogen --release --prerelease --publish --publishTag alpha --push",
27
- "start": "node bin/ipx.js",
28
- "test": "pnpm lint && pnpm test:types && vitest run --coverage",
29
- "test:types": "tsc --noEmit"
30
- },
31
23
  "dependencies": {
32
- "pathe": "^2.0.3",
33
- "sharp": "^0.35.2",
34
- "srvx": "^0.11.17"
24
+ "sharp": "^0.35.5",
25
+ "srvx": "^1.0.5"
35
26
  },
36
27
  "devDependencies": {
37
- "@fastify/accept-negotiator": "^2.0.1",
28
+ "@fastify/accept-negotiator": "^2.1.1",
38
29
  "@types/etag": "^1.8.4",
39
30
  "@types/is-valid-path": "^0.1.2",
40
- "@vitest/coverage-v8": "^4.1.9",
31
+ "@vitest/coverage-v8": "^5.0.3",
41
32
  "automd": "^0.4.3",
42
33
  "changelogen": "^0.6.2",
43
- "eslint": "^10.5.0",
34
+ "eslint": "^10.12.0",
44
35
  "eslint-config-unjs": "^0.6.2",
45
36
  "etag": "^1.8.1",
46
- "h3": "2.0.1-rc.22",
47
- "image-meta": "^0.2.2",
48
- "obuild": "^0.4.37",
49
- "oxc-minify": "^0.137.0",
50
- "prettier": "^3.8.4",
51
- "svgo": "^4.0.1",
37
+ "h3": "^2.0.2",
38
+ "image-meta": "^0.3.0",
39
+ "obuild": "^0.4.44",
40
+ "oxc-minify": ">=0.153.0",
41
+ "prettier": "^3.9.10",
42
+ "svgo": "^4.1.0",
52
43
  "typescript": "^6.0.3",
53
44
  "ufo": "^1.6.4",
54
45
  "unstorage": "^1.17.5",
55
- "vitest": "^4.1.9"
46
+ "vitest": "^5.0.3"
56
47
  },
57
48
  "peerDependencies": {
58
49
  "unstorage": "*"
@@ -62,5 +53,18 @@
62
53
  "optional": true
63
54
  }
64
55
  },
65
- "packageManager": "pnpm@11.9.0"
66
- }
56
+ "engines": {
57
+ "node": "^20.16.0 || >=22.3.0"
58
+ },
59
+ "scripts": {
60
+ "build": "obuild",
61
+ "gen:operations": "node ./scripts/gen-operations.ts",
62
+ "ipx": "node ./src/cli.ts",
63
+ "lint": "eslint . && prettier -c src test scripts automd.config.ts",
64
+ "lint:fix": "automd && eslint . --fix && prettier -w src test scripts automd.config.ts",
65
+ "release": "pnpm test && pnpm build && changelogen --release --push && pnpm publish --tag latest",
66
+ "start": "node bin/ipx.js",
67
+ "test": "pnpm lint && pnpm test:types && vitest run --coverage",
68
+ "test:types": "tsc --noEmit"
69
+ }
70
+ }