ipx 0.7.2 → 0.9.2

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
@@ -6,7 +6,7 @@
6
6
 
7
7
  High performance, secure and easy to use image proxy based on [sharp](https://github.com/lovell/sharp) and [libvips](https://github.com/libvips/libvips).
8
8
 
9
- <h2 align="center">Usage</h2>
9
+ ## Usage
10
10
 
11
11
  ### Quick Start
12
12
 
@@ -85,6 +85,6 @@ Config can be customized using `IPX_*` environment variables.
85
85
  - `IPX_DOMAINS`
86
86
  - Default: `[]`
87
87
 
88
- <h2 align="center">License</h2>
88
+ ## License
89
89
 
90
90
  MIT
package/bin/ipx.mjs ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/cli.mjs'
@@ -1,38 +1,29 @@
1
- #!/usr/bin/env node
2
-
3
1
  'use strict';
4
2
 
5
- const consola = require('consola');
6
- const listhen = require('listhen');
7
- const Sharp = require('sharp');
8
3
  const defu = require('defu');
9
4
  const imageMeta = require('image-meta');
10
5
  const ufo = require('ufo');
11
- const path = require('path');
6
+ const fs = require('fs');
7
+ const pathe = require('pathe');
12
8
  const isValidPath = require('is-valid-path');
13
- const fsExtra = require('fs-extra');
14
- const destr = require('destr');
15
9
  const http = require('http');
16
10
  const https = require('https');
17
- const fetch = require('node-fetch');
11
+ const ohmyfetch = require('ohmyfetch');
12
+ const destr = require('destr');
18
13
  const getEtag = require('etag');
19
14
  const xss = require('xss');
20
15
 
21
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
16
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
22
17
 
23
- const consola__default = /*#__PURE__*/_interopDefaultLegacy(consola);
24
- const Sharp__default = /*#__PURE__*/_interopDefaultLegacy(Sharp);
25
18
  const defu__default = /*#__PURE__*/_interopDefaultLegacy(defu);
26
- const imageMeta__default = /*#__PURE__*/_interopDefaultLegacy(imageMeta);
27
19
  const isValidPath__default = /*#__PURE__*/_interopDefaultLegacy(isValidPath);
28
- const destr__default = /*#__PURE__*/_interopDefaultLegacy(destr);
29
20
  const http__default = /*#__PURE__*/_interopDefaultLegacy(http);
30
21
  const https__default = /*#__PURE__*/_interopDefaultLegacy(https);
31
- const fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
22
+ const destr__default = /*#__PURE__*/_interopDefaultLegacy(destr);
32
23
  const getEtag__default = /*#__PURE__*/_interopDefaultLegacy(getEtag);
33
24
  const xss__default = /*#__PURE__*/_interopDefaultLegacy(xss);
34
25
 
35
- const Handlers = /*#__PURE__*/Object.freeze({
26
+ const Handlers = {
36
27
  __proto__: null,
37
28
  get quality () { return quality; },
38
29
  get fit () { return fit; },
@@ -64,11 +55,10 @@ const Handlers = /*#__PURE__*/Object.freeze({
64
55
  get w () { return w; },
65
56
  get h () { return h; },
66
57
  get s () { return s; }
67
- });
58
+ };
68
59
 
69
60
  function getEnv(name, defaultValue) {
70
- var _a;
71
- return (_a = destr__default['default'](process.env[name])) != null ? _a : defaultValue;
61
+ return destr__default(process.env[name]) ?? defaultValue;
72
62
  }
73
63
  function cachedPromise(fn) {
74
64
  let p;
@@ -90,15 +80,15 @@ function createError(message, statusCode) {
90
80
  }
91
81
 
92
82
  const createFilesystemSource = (options) => {
93
- const rootDir = path.resolve(options.dir);
83
+ const rootDir = pathe.resolve(options.dir);
94
84
  return async (id) => {
95
- const fsPath = path.resolve(path.join(rootDir, id));
96
- if (!isValidPath__default['default'](id) || id.includes("..") || !fsPath.startsWith(rootDir)) {
85
+ const fsPath = pathe.resolve(pathe.join(rootDir, id));
86
+ if (!isValidPath__default(id) || id.includes("..") || !fsPath.startsWith(rootDir)) {
97
87
  throw createError("Forbidden path:" + id, 403);
98
88
  }
99
89
  let stats;
100
90
  try {
101
- stats = await fsExtra.stat(fsPath);
91
+ stats = await fs.promises.stat(fsPath);
102
92
  } catch (err) {
103
93
  if (err.code === "ENOENT") {
104
94
  throw createError("File not found: " + fsPath, 404);
@@ -112,28 +102,28 @@ const createFilesystemSource = (options) => {
112
102
  return {
113
103
  mtime: stats.mtime,
114
104
  maxAge: options.maxAge || 300,
115
- getData: cachedPromise(() => fsExtra.readFile(fsPath))
105
+ getData: cachedPromise(() => fs.promises.readFile(fsPath))
116
106
  };
117
107
  };
118
108
  };
119
109
 
120
110
  const createHTTPSource = (options) => {
121
- const httpsAgent = new https__default['default'].Agent({ keepAlive: true });
122
- const httpAgent = new http__default['default'].Agent({ keepAlive: true });
111
+ const httpsAgent = new https__default.Agent({ keepAlive: true });
112
+ const httpAgent = new http__default.Agent({ keepAlive: true });
123
113
  let domains = options.domains || [];
124
114
  if (typeof domains === "string") {
125
115
  domains = domains.split(",").map((s) => s.trim());
126
116
  }
127
117
  const hosts = domains.map((domain) => ufo.parseURL(domain, "https://").host);
128
118
  return async (id, reqOptions) => {
129
- const parsedUrl = ufo.parseURL(id, "https://");
130
- if (!parsedUrl.host) {
119
+ const url = new URL(id);
120
+ if (!url.hostname) {
131
121
  throw createError("Hostname is missing: " + id, 403);
132
122
  }
133
- if (!(reqOptions == null ? void 0 : reqOptions.bypassDomain) && !hosts.find((host) => parsedUrl.host === host)) {
134
- throw createError("Forbidden host: " + parsedUrl.host, 403);
123
+ if (!reqOptions?.bypassDomain && !hosts.find((host) => url.hostname === host)) {
124
+ throw createError("Forbidden host: " + url.hostname, 403);
135
125
  }
136
- const response = await fetch__default['default'](id, {
126
+ const response = await ohmyfetch.fetch(id, {
137
127
  agent: id.startsWith("https") ? httpsAgent : httpAgent
138
128
  });
139
129
  if (!response.ok) {
@@ -161,7 +151,7 @@ const createHTTPSource = (options) => {
161
151
  };
162
152
 
163
153
  function VArg(arg) {
164
- return destr__default['default'](arg);
154
+ return destr__default(arg);
165
155
  }
166
156
  function parseArgs(args, mappers) {
167
157
  const vargs = args.split("_");
@@ -385,7 +375,7 @@ function createIPX(userOptions) {
385
375
  alias: getEnv("IPX_ALIAS", {}),
386
376
  sharp: {}
387
377
  };
388
- const options = defu__default['default'](userOptions, defaults);
378
+ const options = defu__default(userOptions, defaults);
389
379
  options.alias = Object.fromEntries(Object.entries(options.alias).map((e) => [ufo.withLeadingSlash(e[0]), e[1]]));
390
380
  const ctx = {
391
381
  sources: {}
@@ -420,7 +410,7 @@ function createIPX(userOptions) {
420
410
  const getData = cachedPromise(async () => {
421
411
  const src = await getSrc();
422
412
  const data = await src.getData();
423
- const meta = imageMeta__default['default'](data);
413
+ const meta = imageMeta.imageMeta(data);
424
414
  const mFormat = modifiers.f || modifiers.format;
425
415
  let format = mFormat || meta.type;
426
416
  if (format === "jpg") {
@@ -437,7 +427,8 @@ function createIPX(userOptions) {
437
427
  if (animated) {
438
428
  format = "webp";
439
429
  }
440
- let sharp = Sharp__default['default'](data, { animated });
430
+ const Sharp = await import('sharp').then((r) => r.default || r);
431
+ let sharp = Sharp(data, { animated });
441
432
  Object.assign(sharp.options, options.sharp);
442
433
  const handlers = Object.entries(modifiers).map(([name, args]) => ({ handler: getHandler(name), name, args })).filter((h) => h.handler).sort((a, b) => {
443
434
  const aKey = (a.handler.order || a.name || "").toString();
@@ -505,7 +496,7 @@ async function _handleRequest(req, ipx) {
505
496
  res.headers["Cache-Control"] = `max-age=${+src.maxAge}, public, s-maxage=${+src.maxAge}`;
506
497
  }
507
498
  const { data, format } = await img.data();
508
- const etag = getEtag__default['default'](data);
499
+ const etag = getEtag__default(data);
509
500
  res.headers.ETag = etag;
510
501
  if (etag && req.headers["if-none-match"] === etag) {
511
502
  res.statusCode = 304;
@@ -520,7 +511,7 @@ async function _handleRequest(req, ipx) {
520
511
  function handleRequest(req, ipx) {
521
512
  return _handleRequest(req, ipx).catch((err) => {
522
513
  const statusCode = parseInt(err.statusCode) || 500;
523
- const statusMessage = err.statusMessage ? xss__default['default'](err.statusMessage) : `IPX Error (${statusCode})`;
514
+ const statusMessage = err.statusMessage ? xss__default(err.statusMessage) : `IPX Error (${statusCode})`;
524
515
  if (process.env.NODE_ENV !== "production" && statusCode === 500) {
525
516
  console.error(err);
526
517
  }
@@ -545,14 +536,6 @@ function createIPXMiddleware(ipx) {
545
536
  };
546
537
  }
547
538
 
548
- async function main() {
549
- const ipx = createIPX({});
550
- const middleware = createIPXMiddleware(ipx);
551
- await listhen.listen(middleware, {
552
- clipboard: false
553
- });
554
- }
555
- main().catch((err) => {
556
- consola__default['default'].error(err);
557
- process.exit(1);
558
- });
539
+ exports.createIPX = createIPX;
540
+ exports.createIPXMiddleware = createIPXMiddleware;
541
+ exports.handleRequest = handleRequest;