ipx 0.9.4 → 0.9.6

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/LICENSE CHANGED
File without changes
@@ -5,7 +5,6 @@ const imageMeta = require('image-meta');
5
5
  const ufo = require('ufo');
6
6
  const fs = require('fs');
7
7
  const pathe = require('pathe');
8
- const isValidPath = require('is-valid-path');
9
8
  const http = require('http');
10
9
  const https = require('https');
11
10
  const ohmyfetch = require('ohmyfetch');
@@ -16,7 +15,6 @@ const xss = require('xss');
16
15
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
17
16
 
18
17
  const defu__default = /*#__PURE__*/_interopDefaultLegacy(defu);
19
- const isValidPath__default = /*#__PURE__*/_interopDefaultLegacy(isValidPath);
20
18
  const http__default = /*#__PURE__*/_interopDefaultLegacy(http);
21
19
  const https__default = /*#__PURE__*/_interopDefaultLegacy(https);
22
20
  const destr__default = /*#__PURE__*/_interopDefaultLegacy(destr);
@@ -74,9 +72,9 @@ function cachedPromise(fn) {
74
72
  }
75
73
  class IPXError extends Error {
76
74
  }
77
- function createError(message, statusCode) {
78
- const err = new IPXError(message);
79
- err.statusMessage = "IPX: " + message;
75
+ function createError(statusMessage, statusCode, trace) {
76
+ const err = new IPXError(statusMessage + (trace ? ` (${trace})` : ""));
77
+ err.statusMessage = "IPX: " + statusMessage;
80
78
  err.statusCode = statusCode;
81
79
  return err;
82
80
  }
@@ -85,29 +83,35 @@ const createFilesystemSource = (options) => {
85
83
  const rootDir = pathe.resolve(options.dir);
86
84
  return async (id) => {
87
85
  const fsPath = pathe.resolve(pathe.join(rootDir, id));
88
- if (!isValidPath__default(id) || id.includes("..") || !fsPath.startsWith(rootDir)) {
89
- throw createError("Forbidden path:" + id, 403);
86
+ if (!isValidPath(fsPath) || !fsPath.startsWith(rootDir)) {
87
+ throw createError("Forbidden path", 403, id);
90
88
  }
91
89
  let stats;
92
90
  try {
93
91
  stats = await fs.promises.stat(fsPath);
94
92
  } catch (err) {
95
93
  if (err.code === "ENOENT") {
96
- throw createError("File not found: " + fsPath, 404);
94
+ throw createError("File not found", 404, fsPath);
97
95
  } else {
98
- throw createError("File access error for " + fsPath + ":" + err.code, 403);
96
+ throw createError("File access error " + err.code, 403, fsPath);
99
97
  }
100
98
  }
101
99
  if (!stats.isFile()) {
102
- throw createError("Path should be a file: " + fsPath, 400);
100
+ throw createError("Path should be a file", 400, fsPath);
103
101
  }
104
102
  return {
105
103
  mtime: stats.mtime,
106
- maxAge: options.maxAge || 300,
104
+ maxAge: options.maxAge,
107
105
  getData: cachedPromise(() => fs.promises.readFile(fsPath))
108
106
  };
109
107
  };
110
108
  };
109
+ function isValidPath(fp) {
110
+ if (/[<>:"|?*]/.test(fp)) {
111
+ return false;
112
+ }
113
+ return true;
114
+ }
111
115
 
112
116
  const createHTTPSource = (options) => {
113
117
  const httpsAgent = new https__default.Agent({ keepAlive: true });
@@ -120,18 +124,19 @@ const createHTTPSource = (options) => {
120
124
  return async (id, reqOptions) => {
121
125
  const url = new URL(id);
122
126
  if (!url.hostname) {
123
- throw createError("Hostname is missing: " + id, 403);
127
+ throw createError("Hostname is missing", 403, id);
124
128
  }
125
129
  if (!reqOptions?.bypassDomain && !hosts.find((host) => url.hostname === host)) {
126
- throw createError("Forbidden host: " + url.hostname, 403);
130
+ throw createError("Forbidden host", 403, url.hostname);
127
131
  }
128
132
  const response = await ohmyfetch.fetch(id, {
129
- agent: id.startsWith("https") ? httpsAgent : httpAgent
133
+ agent: id.startsWith("https") ? httpsAgent : httpAgent,
134
+ ...options.fetchOptions
130
135
  });
131
136
  if (!response.ok) {
132
- throw createError(response.statusText || "fetch error", response.status || 500);
137
+ throw createError("Fetch error", response.status || 500, response.statusText);
133
138
  }
134
- let maxAge = options.maxAge || 300;
139
+ let maxAge = options.maxAge;
135
140
  const _cacheControl = response.headers.get("cache-control");
136
141
  if (_cacheControl) {
137
142
  const m = _cacheControl.match(/max-age=(\d+)/);
@@ -147,7 +152,7 @@ const createHTTPSource = (options) => {
147
152
  return {
148
153
  mtime,
149
154
  maxAge,
150
- getData: cachedPromise(() => response.buffer())
155
+ getData: cachedPromise(() => response.arrayBuffer().then((ab) => Buffer.from(ab)))
151
156
  };
152
157
  };
153
158
  };
@@ -384,12 +389,14 @@ const h = height;
384
389
  const s = resize;
385
390
  const pos = position;
386
391
 
387
- const SUPPORTED_FORMATS = ["jpeg", "png", "webp", "avif", "tiff"];
392
+ const SUPPORTED_FORMATS = ["jpeg", "png", "webp", "avif", "tiff", "gif"];
388
393
  function createIPX(userOptions) {
389
394
  const defaults = {
390
395
  dir: getEnv("IPX_DIR", "."),
391
396
  domains: getEnv("IPX_DOMAINS", []),
392
397
  alias: getEnv("IPX_ALIAS", {}),
398
+ fetchOptions: getEnv("IPX_FETCH_OPTIONS", {}),
399
+ maxAge: getEnv("IPX_MAX_AGE", 300),
393
400
  sharp: {}
394
401
  };
395
402
  const options = defu__default(userOptions, defaults);
@@ -399,12 +406,15 @@ function createIPX(userOptions) {
399
406
  };
400
407
  if (options.dir) {
401
408
  ctx.sources.filesystem = createFilesystemSource({
402
- dir: options.dir
409
+ dir: options.dir,
410
+ maxAge: options.maxAge
403
411
  });
404
412
  }
405
413
  if (options.domains) {
406
414
  ctx.sources.http = createHTTPSource({
407
- domains: options.domains
415
+ domains: options.domains,
416
+ fetchOptions: options.fetchOptions,
417
+ maxAge: options.maxAge
408
418
  });
409
419
  }
410
420
  return function ipx(id, modifiers = {}, reqOptions = {}) {
@@ -420,7 +430,7 @@ function createIPX(userOptions) {
420
430
  const getSrc = cachedPromise(() => {
421
431
  const source = ufo.hasProtocol(id) ? "http" : "filesystem";
422
432
  if (!ctx.sources[source]) {
423
- throw createError("Unknown source: " + source, 400);
433
+ throw createError("Unknown source", 400, source);
424
434
  }
425
435
  return ctx.sources[source](id, reqOptions);
426
436
  });
@@ -440,10 +450,7 @@ function createIPX(userOptions) {
440
450
  meta
441
451
  };
442
452
  }
443
- const animated = modifiers.animated !== void 0 || modifiers.a !== void 0;
444
- if (animated) {
445
- format = "webp";
446
- }
453
+ const animated = modifiers.animated !== void 0 || modifiers.a !== void 0 || format === "gif";
447
454
  const Sharp = await import('sharp').then((r) => r.default || r);
448
455
  let sharp = Sharp(data, { animated });
449
456
  Object.assign(sharp.options, options.sharp);
@@ -476,6 +483,8 @@ function createIPX(userOptions) {
476
483
  };
477
484
  }
478
485
 
486
+ const MODIFIER_SEP = /[,&]/g;
487
+ const MODIFIER_VAL_SEP = /[_=:]/g;
479
488
  async function _handleRequest(req, ipx) {
480
489
  const res = {
481
490
  statusCode: 200,
@@ -483,19 +492,19 @@ async function _handleRequest(req, ipx) {
483
492
  headers: {},
484
493
  body: ""
485
494
  };
486
- const [modifiersStr = "", ...idSegments] = req.url.substr(1).split("/");
487
- const id = ufo.decode(idSegments.join("/"));
495
+ const [modifiersStr = "", ...idSegments] = req.url.substring(1).split("/");
496
+ const id = safeString(ufo.decode(idSegments.join("/")));
488
497
  if (!modifiersStr) {
489
- throw createError("Modifiers is missing in path: " + req.url, 400);
498
+ throw createError("Modifiers are missing", 400, req.url);
490
499
  }
491
500
  if (!id || id === "/") {
492
- throw createError("Resource id is missing: " + req.url, 400);
501
+ throw createError("Resource id is missing", 400, req.url);
493
502
  }
494
503
  const modifiers = /* @__PURE__ */ Object.create(null);
495
504
  if (modifiersStr !== "_") {
496
- for (const p of modifiersStr.split(",")) {
497
- const [key, value = ""] = p.split("_");
498
- modifiers[key] = ufo.decode(value);
505
+ for (const p of modifiersStr.split(MODIFIER_SEP)) {
506
+ const [key, value = ""] = p.split(MODIFIER_VAL_SEP);
507
+ modifiers[safeString(key)] = safeString(ufo.decode(value));
499
508
  }
500
509
  }
501
510
  const img = ipx(id, modifiers, req.options);
@@ -509,7 +518,7 @@ async function _handleRequest(req, ipx) {
509
518
  }
510
519
  res.headers["Last-Modified"] = +src.mtime + "";
511
520
  }
512
- if (src.maxAge !== void 0) {
521
+ if (typeof src.maxAge === "number") {
513
522
  res.headers["Cache-Control"] = `max-age=${+src.maxAge}, public, s-maxage=${+src.maxAge}`;
514
523
  }
515
524
  const { data, format } = await img.data();
@@ -523,21 +532,21 @@ async function _handleRequest(req, ipx) {
523
532
  res.headers["Content-Type"] = `image/${format}`;
524
533
  }
525
534
  res.body = data;
526
- return res;
535
+ return sanetizeReponse(res);
527
536
  }
528
537
  function handleRequest(req, ipx) {
529
538
  return _handleRequest(req, ipx).catch((err) => {
530
539
  const statusCode = parseInt(err.statusCode) || 500;
531
- const statusMessage = err.statusMessage ? xss__default(err.statusMessage) : `IPX Error (${statusCode})`;
540
+ const statusMessage = err.statusMessage ? err.statusMessage : `IPX Error (${statusCode})`;
532
541
  if (process.env.NODE_ENV !== "production" && statusCode === 500) {
533
542
  console.error(err);
534
543
  }
535
- return {
544
+ return sanetizeReponse({
536
545
  statusCode,
537
546
  statusMessage,
538
- body: statusMessage,
547
+ body: "IPX Error: " + err,
539
548
  headers: {}
540
- };
549
+ });
541
550
  });
542
551
  }
543
552
  function createIPXMiddleware(ipx) {
@@ -552,6 +561,24 @@ function createIPXMiddleware(ipx) {
552
561
  });
553
562
  };
554
563
  }
564
+ function sanetizeReponse(res) {
565
+ return {
566
+ statusCode: res.statusCode || 200,
567
+ statusMessage: res.statusMessage ? safeString(res.statusMessage) : "OK",
568
+ headers: safeStringObject(res.headers || {}),
569
+ body: typeof res.body === "string" ? xss__default(safeString(res.body)) : res.body || ""
570
+ };
571
+ }
572
+ function safeString(input) {
573
+ return JSON.stringify(input).replace(/^"|"$/g, "");
574
+ }
575
+ function safeStringObject(input) {
576
+ const dst = {};
577
+ for (const key in input) {
578
+ dst[key] = safeString(input[key]);
579
+ }
580
+ return dst;
581
+ }
555
582
 
556
583
  exports.createIPX = createIPX;
557
584
  exports.createIPXMiddleware = createIPXMiddleware;
@@ -3,7 +3,6 @@ import { imageMeta } from 'image-meta';
3
3
  import { parseURL, withLeadingSlash, hasProtocol, joinURL, decode } from 'ufo';
4
4
  import { promises } from 'fs';
5
5
  import { resolve, join } from 'pathe';
6
- import isValidPath from 'is-valid-path';
7
6
  import http from 'http';
8
7
  import https from 'https';
9
8
  import { fetch } from 'ohmyfetch';
@@ -62,9 +61,9 @@ function cachedPromise(fn) {
62
61
  }
63
62
  class IPXError extends Error {
64
63
  }
65
- function createError(message, statusCode) {
66
- const err = new IPXError(message);
67
- err.statusMessage = "IPX: " + message;
64
+ function createError(statusMessage, statusCode, trace) {
65
+ const err = new IPXError(statusMessage + (trace ? ` (${trace})` : ""));
66
+ err.statusMessage = "IPX: " + statusMessage;
68
67
  err.statusCode = statusCode;
69
68
  return err;
70
69
  }
@@ -73,29 +72,35 @@ const createFilesystemSource = (options) => {
73
72
  const rootDir = resolve(options.dir);
74
73
  return async (id) => {
75
74
  const fsPath = resolve(join(rootDir, id));
76
- if (!isValidPath(id) || id.includes("..") || !fsPath.startsWith(rootDir)) {
77
- throw createError("Forbidden path:" + id, 403);
75
+ if (!isValidPath(fsPath) || !fsPath.startsWith(rootDir)) {
76
+ throw createError("Forbidden path", 403, id);
78
77
  }
79
78
  let stats;
80
79
  try {
81
80
  stats = await promises.stat(fsPath);
82
81
  } catch (err) {
83
82
  if (err.code === "ENOENT") {
84
- throw createError("File not found: " + fsPath, 404);
83
+ throw createError("File not found", 404, fsPath);
85
84
  } else {
86
- throw createError("File access error for " + fsPath + ":" + err.code, 403);
85
+ throw createError("File access error " + err.code, 403, fsPath);
87
86
  }
88
87
  }
89
88
  if (!stats.isFile()) {
90
- throw createError("Path should be a file: " + fsPath, 400);
89
+ throw createError("Path should be a file", 400, fsPath);
91
90
  }
92
91
  return {
93
92
  mtime: stats.mtime,
94
- maxAge: options.maxAge || 300,
93
+ maxAge: options.maxAge,
95
94
  getData: cachedPromise(() => promises.readFile(fsPath))
96
95
  };
97
96
  };
98
97
  };
98
+ function isValidPath(fp) {
99
+ if (/[<>:"|?*]/.test(fp)) {
100
+ return false;
101
+ }
102
+ return true;
103
+ }
99
104
 
100
105
  const createHTTPSource = (options) => {
101
106
  const httpsAgent = new https.Agent({ keepAlive: true });
@@ -108,18 +113,19 @@ const createHTTPSource = (options) => {
108
113
  return async (id, reqOptions) => {
109
114
  const url = new URL(id);
110
115
  if (!url.hostname) {
111
- throw createError("Hostname is missing: " + id, 403);
116
+ throw createError("Hostname is missing", 403, id);
112
117
  }
113
118
  if (!reqOptions?.bypassDomain && !hosts.find((host) => url.hostname === host)) {
114
- throw createError("Forbidden host: " + url.hostname, 403);
119
+ throw createError("Forbidden host", 403, url.hostname);
115
120
  }
116
121
  const response = await fetch(id, {
117
- agent: id.startsWith("https") ? httpsAgent : httpAgent
122
+ agent: id.startsWith("https") ? httpsAgent : httpAgent,
123
+ ...options.fetchOptions
118
124
  });
119
125
  if (!response.ok) {
120
- throw createError(response.statusText || "fetch error", response.status || 500);
126
+ throw createError("Fetch error", response.status || 500, response.statusText);
121
127
  }
122
- let maxAge = options.maxAge || 300;
128
+ let maxAge = options.maxAge;
123
129
  const _cacheControl = response.headers.get("cache-control");
124
130
  if (_cacheControl) {
125
131
  const m = _cacheControl.match(/max-age=(\d+)/);
@@ -135,7 +141,7 @@ const createHTTPSource = (options) => {
135
141
  return {
136
142
  mtime,
137
143
  maxAge,
138
- getData: cachedPromise(() => response.buffer())
144
+ getData: cachedPromise(() => response.arrayBuffer().then((ab) => Buffer.from(ab)))
139
145
  };
140
146
  };
141
147
  };
@@ -372,12 +378,14 @@ const h = height;
372
378
  const s = resize;
373
379
  const pos = position;
374
380
 
375
- const SUPPORTED_FORMATS = ["jpeg", "png", "webp", "avif", "tiff"];
381
+ const SUPPORTED_FORMATS = ["jpeg", "png", "webp", "avif", "tiff", "gif"];
376
382
  function createIPX(userOptions) {
377
383
  const defaults = {
378
384
  dir: getEnv("IPX_DIR", "."),
379
385
  domains: getEnv("IPX_DOMAINS", []),
380
386
  alias: getEnv("IPX_ALIAS", {}),
387
+ fetchOptions: getEnv("IPX_FETCH_OPTIONS", {}),
388
+ maxAge: getEnv("IPX_MAX_AGE", 300),
381
389
  sharp: {}
382
390
  };
383
391
  const options = defu(userOptions, defaults);
@@ -387,12 +395,15 @@ function createIPX(userOptions) {
387
395
  };
388
396
  if (options.dir) {
389
397
  ctx.sources.filesystem = createFilesystemSource({
390
- dir: options.dir
398
+ dir: options.dir,
399
+ maxAge: options.maxAge
391
400
  });
392
401
  }
393
402
  if (options.domains) {
394
403
  ctx.sources.http = createHTTPSource({
395
- domains: options.domains
404
+ domains: options.domains,
405
+ fetchOptions: options.fetchOptions,
406
+ maxAge: options.maxAge
396
407
  });
397
408
  }
398
409
  return function ipx(id, modifiers = {}, reqOptions = {}) {
@@ -408,7 +419,7 @@ function createIPX(userOptions) {
408
419
  const getSrc = cachedPromise(() => {
409
420
  const source = hasProtocol(id) ? "http" : "filesystem";
410
421
  if (!ctx.sources[source]) {
411
- throw createError("Unknown source: " + source, 400);
422
+ throw createError("Unknown source", 400, source);
412
423
  }
413
424
  return ctx.sources[source](id, reqOptions);
414
425
  });
@@ -428,10 +439,7 @@ function createIPX(userOptions) {
428
439
  meta
429
440
  };
430
441
  }
431
- const animated = modifiers.animated !== void 0 || modifiers.a !== void 0;
432
- if (animated) {
433
- format = "webp";
434
- }
442
+ const animated = modifiers.animated !== void 0 || modifiers.a !== void 0 || format === "gif";
435
443
  const Sharp = await import('sharp').then((r) => r.default || r);
436
444
  let sharp = Sharp(data, { animated });
437
445
  Object.assign(sharp.options, options.sharp);
@@ -464,6 +472,8 @@ function createIPX(userOptions) {
464
472
  };
465
473
  }
466
474
 
475
+ const MODIFIER_SEP = /[,&]/g;
476
+ const MODIFIER_VAL_SEP = /[_=:]/g;
467
477
  async function _handleRequest(req, ipx) {
468
478
  const res = {
469
479
  statusCode: 200,
@@ -471,19 +481,19 @@ async function _handleRequest(req, ipx) {
471
481
  headers: {},
472
482
  body: ""
473
483
  };
474
- const [modifiersStr = "", ...idSegments] = req.url.substr(1).split("/");
475
- const id = decode(idSegments.join("/"));
484
+ const [modifiersStr = "", ...idSegments] = req.url.substring(1).split("/");
485
+ const id = safeString(decode(idSegments.join("/")));
476
486
  if (!modifiersStr) {
477
- throw createError("Modifiers is missing in path: " + req.url, 400);
487
+ throw createError("Modifiers are missing", 400, req.url);
478
488
  }
479
489
  if (!id || id === "/") {
480
- throw createError("Resource id is missing: " + req.url, 400);
490
+ throw createError("Resource id is missing", 400, req.url);
481
491
  }
482
492
  const modifiers = /* @__PURE__ */ Object.create(null);
483
493
  if (modifiersStr !== "_") {
484
- for (const p of modifiersStr.split(",")) {
485
- const [key, value = ""] = p.split("_");
486
- modifiers[key] = decode(value);
494
+ for (const p of modifiersStr.split(MODIFIER_SEP)) {
495
+ const [key, value = ""] = p.split(MODIFIER_VAL_SEP);
496
+ modifiers[safeString(key)] = safeString(decode(value));
487
497
  }
488
498
  }
489
499
  const img = ipx(id, modifiers, req.options);
@@ -497,7 +507,7 @@ async function _handleRequest(req, ipx) {
497
507
  }
498
508
  res.headers["Last-Modified"] = +src.mtime + "";
499
509
  }
500
- if (src.maxAge !== void 0) {
510
+ if (typeof src.maxAge === "number") {
501
511
  res.headers["Cache-Control"] = `max-age=${+src.maxAge}, public, s-maxage=${+src.maxAge}`;
502
512
  }
503
513
  const { data, format } = await img.data();
@@ -511,21 +521,21 @@ async function _handleRequest(req, ipx) {
511
521
  res.headers["Content-Type"] = `image/${format}`;
512
522
  }
513
523
  res.body = data;
514
- return res;
524
+ return sanetizeReponse(res);
515
525
  }
516
526
  function handleRequest(req, ipx) {
517
527
  return _handleRequest(req, ipx).catch((err) => {
518
528
  const statusCode = parseInt(err.statusCode) || 500;
519
- const statusMessage = err.statusMessage ? xss(err.statusMessage) : `IPX Error (${statusCode})`;
529
+ const statusMessage = err.statusMessage ? err.statusMessage : `IPX Error (${statusCode})`;
520
530
  if (process.env.NODE_ENV !== "production" && statusCode === 500) {
521
531
  console.error(err);
522
532
  }
523
- return {
533
+ return sanetizeReponse({
524
534
  statusCode,
525
535
  statusMessage,
526
- body: statusMessage,
536
+ body: "IPX Error: " + err,
527
537
  headers: {}
528
- };
538
+ });
529
539
  });
530
540
  }
531
541
  function createIPXMiddleware(ipx) {
@@ -540,5 +550,23 @@ function createIPXMiddleware(ipx) {
540
550
  });
541
551
  };
542
552
  }
553
+ function sanetizeReponse(res) {
554
+ return {
555
+ statusCode: res.statusCode || 200,
556
+ statusMessage: res.statusMessage ? safeString(res.statusMessage) : "OK",
557
+ headers: safeStringObject(res.headers || {}),
558
+ body: typeof res.body === "string" ? xss(safeString(res.body)) : res.body || ""
559
+ };
560
+ }
561
+ function safeString(input) {
562
+ return JSON.stringify(input).replace(/^"|"$/g, "");
563
+ }
564
+ function safeStringObject(input) {
565
+ const dst = {};
566
+ for (const key in input) {
567
+ dst[key] = safeString(input[key]);
568
+ }
569
+ return dst;
570
+ }
543
571
 
544
572
  export { createIPXMiddleware as a, createIPX as c, handleRequest as h };
package/dist/cli.cjs CHANGED
@@ -8,7 +8,6 @@ require('image-meta');
8
8
  require('ufo');
9
9
  require('fs');
10
10
  require('pathe');
11
- require('is-valid-path');
12
11
  require('http');
13
12
  require('https');
14
13
  require('ohmyfetch');
package/dist/cli.mjs CHANGED
@@ -6,7 +6,6 @@ import 'image-meta';
6
6
  import 'ufo';
7
7
  import 'fs';
8
8
  import 'pathe';
9
- import 'is-valid-path';
10
9
  import 'http';
11
10
  import 'https';
12
11
  import 'ohmyfetch';
package/dist/index.cjs CHANGED
@@ -8,7 +8,6 @@ require('image-meta');
8
8
  require('ufo');
9
9
  require('fs');
10
10
  require('pathe');
11
- require('is-valid-path');
12
11
  require('http');
13
12
  require('https');
14
13
  require('ohmyfetch');
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ interface SourceData {
6
6
  getData: () => Promise<Buffer>;
7
7
  }
8
8
  declare type Source = (src: string, reqOptions?: any) => Promise<SourceData>;
9
- declare type SourceFactory = (options?: any) => Source;
9
+ declare type SourceFactory<T = Record<string, any>> = (options: T) => Source;
10
10
 
11
11
  interface ImageMeta {
12
12
  width: number;
@@ -27,8 +27,10 @@ declare type IPX = (id: string, modifiers?: Record<string, string>, reqOptions?:
27
27
  };
28
28
  interface IPXOptions {
29
29
  dir?: false | string;
30
+ maxAge?: number;
30
31
  domains?: false | string[];
31
32
  alias: Record<string, string>;
33
+ fetchOptions: RequestInit;
32
34
  sharp?: {
33
35
  [key: string]: any;
34
36
  };
package/dist/index.mjs CHANGED
@@ -4,7 +4,6 @@ import 'image-meta';
4
4
  import 'ufo';
5
5
  import 'fs';
6
6
  import 'pathe';
7
- import 'is-valid-path';
8
7
  import 'http';
9
8
  import 'https';
10
9
  import 'ohmyfetch';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ipx",
3
- "version": "0.9.4",
3
+ "version": "0.9.6",
4
4
  "repository": "unjs/ipx",
5
5
  "license": "MIT",
6
6
  "exports": {
@@ -17,43 +17,42 @@
17
17
  "dist",
18
18
  "bin"
19
19
  ],
20
- "scripts": {
21
- "build": "unbuild",
22
- "dev": "nodemon",
23
- "lint": "eslint --ext .ts .",
24
- "prepack": "yarn build",
25
- "release": "yarn test && standard-version && git push --follow-tags && npm publish",
26
- "start": "node bin/ipx.js",
27
- "test": "yarn lint && jest"
28
- },
29
20
  "dependencies": {
30
21
  "consola": "^2.15.3",
31
- "defu": "^5.0.1",
32
- "destr": "^1.1.0",
22
+ "defu": "^6.0.0",
23
+ "destr": "^1.1.1",
33
24
  "etag": "^1.8.1",
34
25
  "image-meta": "^0.1.1",
35
- "is-valid-path": "^0.1.1",
36
- "listhen": "^0.2.6",
37
- "ohmyfetch": "^0.4.15",
38
- "pathe": "^0.2.0",
39
- "sharp": "^0.30.1",
40
- "ufo": "^0.7.10",
41
- "xss": "^1.0.10"
26
+ "listhen": "^0.2.13",
27
+ "ohmyfetch": "^0.4.18",
28
+ "pathe": "^0.3.0",
29
+ "sharp": "^0.30.6",
30
+ "ufo": "^0.8.4",
31
+ "xss": "^1.0.13"
42
32
  },
43
33
  "devDependencies": {
44
34
  "@nuxtjs/eslint-config-typescript": "latest",
45
35
  "@types/etag": "latest",
46
36
  "@types/is-valid-path": "latest",
47
- "@types/jest": "latest",
48
37
  "@types/node-fetch": "latest",
49
38
  "@types/sharp": "latest",
39
+ "c8": "latest",
50
40
  "eslint": "latest",
51
- "jest": "latest",
52
41
  "jiti": "latest",
53
42
  "nodemon": "latest",
43
+ "serve-handler": "^6.1.3",
54
44
  "standard-version": "latest",
55
- "ts-jest": "latest",
56
45
  "typescript": "latest",
57
- "unbuild": "latest"
46
+ "unbuild": "latest",
47
+ "vitest": "latest"
48
+ },
49
+ "packageManager": "pnpm@7.3.0",
50
+ "scripts": {
51
+ "build": "unbuild",
52
+ "dev": "nodemon",
53
+ "lint": "eslint --ext .ts .",
54
+ "release": "pnpm test && standard-version && git push --follow-tags && pnpm publish",
55
+ "start": "node bin/ipx.js",
56
+ "test": "pnpm lint && vitest run --coverage"
58
57
  }
59
- }
58
+ }