ipx 0.9.11 → 1.0.0-1
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 +1 -1
- package/dist/cli.cjs +6 -10
- package/dist/cli.mjs +6 -6
- package/dist/index.cjs +4 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.mjs +4 -4
- package/dist/shared/{ipx.eadce322.cjs → ipx.3f4cd42a.cjs} +84 -98
- package/dist/shared/{ipx.72b0591f.mjs → ipx.b3753e41.mjs} +77 -82
- package/package.json +31 -29
package/LICENSE
CHANGED
package/dist/cli.cjs
CHANGED
|
@@ -2,23 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
const consola = require('consola');
|
|
4
4
|
const listhen = require('listhen');
|
|
5
|
-
const middleware = require('./shared/ipx.
|
|
5
|
+
const middleware = require('./shared/ipx.3f4cd42a.cjs');
|
|
6
6
|
require('defu');
|
|
7
7
|
require('image-meta');
|
|
8
8
|
require('ufo');
|
|
9
|
-
require('fs');
|
|
9
|
+
require('node:fs');
|
|
10
10
|
require('pathe');
|
|
11
|
-
require('http');
|
|
12
|
-
require('https');
|
|
11
|
+
require('node:http');
|
|
12
|
+
require('node:https');
|
|
13
13
|
require('ohmyfetch');
|
|
14
14
|
require('destr');
|
|
15
15
|
require('etag');
|
|
16
16
|
require('xss');
|
|
17
17
|
|
|
18
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
|
|
19
|
-
|
|
20
|
-
const consola__default = /*#__PURE__*/_interopDefaultLegacy(consola);
|
|
21
|
-
|
|
22
18
|
async function main() {
|
|
23
19
|
const ipx = middleware.createIPX({});
|
|
24
20
|
const middleware$1 = middleware.createIPXMiddleware(ipx);
|
|
@@ -26,7 +22,7 @@ async function main() {
|
|
|
26
22
|
clipboard: false
|
|
27
23
|
});
|
|
28
24
|
}
|
|
29
|
-
main().catch((
|
|
30
|
-
|
|
25
|
+
main().catch((error) => {
|
|
26
|
+
consola.error(error);
|
|
31
27
|
process.exit(1);
|
|
32
28
|
});
|
package/dist/cli.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import consola from 'consola';
|
|
2
2
|
import { listen } from 'listhen';
|
|
3
|
-
import { c as createIPX, a as createIPXMiddleware } from './shared/ipx.
|
|
3
|
+
import { c as createIPX, a as createIPXMiddleware } from './shared/ipx.b3753e41.mjs';
|
|
4
4
|
import 'defu';
|
|
5
5
|
import 'image-meta';
|
|
6
6
|
import 'ufo';
|
|
7
|
-
import 'fs';
|
|
7
|
+
import 'node:fs';
|
|
8
8
|
import 'pathe';
|
|
9
|
-
import 'http';
|
|
10
|
-
import 'https';
|
|
9
|
+
import 'node:http';
|
|
10
|
+
import 'node:https';
|
|
11
11
|
import 'ohmyfetch';
|
|
12
12
|
import 'destr';
|
|
13
13
|
import 'etag';
|
|
@@ -20,7 +20,7 @@ async function main() {
|
|
|
20
20
|
clipboard: false
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
|
-
main().catch((
|
|
24
|
-
consola.error(
|
|
23
|
+
main().catch((error) => {
|
|
24
|
+
consola.error(error);
|
|
25
25
|
process.exit(1);
|
|
26
26
|
});
|
package/dist/index.cjs
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const middleware = require('./shared/ipx.eadce322.cjs');
|
|
3
|
+
const middleware = require('./shared/ipx.3f4cd42a.cjs');
|
|
6
4
|
require('defu');
|
|
7
5
|
require('image-meta');
|
|
8
6
|
require('ufo');
|
|
9
|
-
require('fs');
|
|
7
|
+
require('node:fs');
|
|
10
8
|
require('pathe');
|
|
11
|
-
require('http');
|
|
12
|
-
require('https');
|
|
9
|
+
require('node:http');
|
|
10
|
+
require('node:https');
|
|
13
11
|
require('ohmyfetch');
|
|
14
12
|
require('destr');
|
|
15
13
|
require('etag');
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { IncomingMessage, ServerResponse } from 'http';
|
|
1
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
2
|
|
|
3
3
|
interface SourceData {
|
|
4
4
|
mtime?: Date;
|
|
5
5
|
maxAge?: number;
|
|
6
6
|
getData: () => Promise<Buffer>;
|
|
7
7
|
}
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
type Source = (source: string, requestOptions?: any) => Promise<SourceData>;
|
|
9
|
+
type SourceFactory<T = Record<string, any>> = (options: T) => Source;
|
|
10
10
|
|
|
11
11
|
interface ImageMeta {
|
|
12
12
|
width: number;
|
|
@@ -17,7 +17,7 @@ interface ImageMeta {
|
|
|
17
17
|
interface IPXCTX {
|
|
18
18
|
sources: Record<string, Source>;
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
type IPX = (id: string, modifiers?: Record<string, string>, requestOptions?: any) => {
|
|
21
21
|
src: () => Promise<SourceData>;
|
|
22
22
|
data: () => Promise<{
|
|
23
23
|
data: Buffer;
|
|
@@ -48,7 +48,7 @@ interface IPXHResponse {
|
|
|
48
48
|
headers: Record<string, string>;
|
|
49
49
|
body: any;
|
|
50
50
|
}
|
|
51
|
-
declare function handleRequest(
|
|
52
|
-
declare function createIPXMiddleware(ipx: IPX): (
|
|
51
|
+
declare function handleRequest(request: IPXHRequest, ipx: IPX): Promise<IPXHResponse>;
|
|
52
|
+
declare function createIPXMiddleware(ipx: IPX): (request: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
53
53
|
|
|
54
54
|
export { IPX, IPXCTX, IPXHRequest, IPXHResponse, IPXOptions, ImageMeta, Source, SourceData, SourceFactory, createIPX, createIPXMiddleware, handleRequest };
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export { c as createIPX, a as createIPXMiddleware, h as handleRequest } from './shared/ipx.
|
|
1
|
+
export { c as createIPX, a as createIPXMiddleware, h as handleRequest } from './shared/ipx.b3753e41.mjs';
|
|
2
2
|
import 'defu';
|
|
3
3
|
import 'image-meta';
|
|
4
4
|
import 'ufo';
|
|
5
|
-
import 'fs';
|
|
5
|
+
import 'node:fs';
|
|
6
6
|
import 'pathe';
|
|
7
|
-
import 'http';
|
|
8
|
-
import 'https';
|
|
7
|
+
import 'node:http';
|
|
8
|
+
import 'node:https';
|
|
9
9
|
import 'ohmyfetch';
|
|
10
10
|
import 'destr';
|
|
11
11
|
import 'etag';
|
|
@@ -3,24 +3,15 @@
|
|
|
3
3
|
const defu = require('defu');
|
|
4
4
|
const imageMeta = require('image-meta');
|
|
5
5
|
const ufo = require('ufo');
|
|
6
|
-
const
|
|
6
|
+
const node_fs = require('node:fs');
|
|
7
7
|
const pathe = require('pathe');
|
|
8
|
-
const http = require('http');
|
|
9
|
-
const https = require('https');
|
|
8
|
+
const http = require('node:http');
|
|
9
|
+
const https = require('node:https');
|
|
10
10
|
const ohmyfetch = require('ohmyfetch');
|
|
11
11
|
const destr = require('destr');
|
|
12
12
|
const getEtag = require('etag');
|
|
13
13
|
const xss = require('xss');
|
|
14
14
|
|
|
15
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
|
|
16
|
-
|
|
17
|
-
const defu__default = /*#__PURE__*/_interopDefaultLegacy(defu);
|
|
18
|
-
const http__default = /*#__PURE__*/_interopDefaultLegacy(http);
|
|
19
|
-
const https__default = /*#__PURE__*/_interopDefaultLegacy(https);
|
|
20
|
-
const destr__default = /*#__PURE__*/_interopDefaultLegacy(destr);
|
|
21
|
-
const getEtag__default = /*#__PURE__*/_interopDefaultLegacy(getEtag);
|
|
22
|
-
const xss__default = /*#__PURE__*/_interopDefaultLegacy(xss);
|
|
23
|
-
|
|
24
15
|
const Handlers = {
|
|
25
16
|
__proto__: null,
|
|
26
17
|
get quality () { return quality; },
|
|
@@ -58,25 +49,25 @@ const Handlers = {
|
|
|
58
49
|
};
|
|
59
50
|
|
|
60
51
|
function getEnv(name, defaultValue) {
|
|
61
|
-
return
|
|
52
|
+
return destr(process.env[name]) ?? defaultValue;
|
|
62
53
|
}
|
|
63
|
-
function cachedPromise(
|
|
54
|
+
function cachedPromise(function_) {
|
|
64
55
|
let p;
|
|
65
|
-
return (...
|
|
56
|
+
return (...arguments_) => {
|
|
66
57
|
if (p) {
|
|
67
58
|
return p;
|
|
68
59
|
}
|
|
69
|
-
p = Promise.resolve(
|
|
60
|
+
p = Promise.resolve(function_(...arguments_));
|
|
70
61
|
return p;
|
|
71
62
|
};
|
|
72
63
|
}
|
|
73
64
|
class IPXError extends Error {
|
|
74
65
|
}
|
|
75
66
|
function createError(statusMessage, statusCode, trace) {
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return
|
|
67
|
+
const error = new IPXError(statusMessage + (trace ? ` (${trace})` : ""));
|
|
68
|
+
error.statusMessage = "IPX: " + statusMessage;
|
|
69
|
+
error.statusCode = statusCode;
|
|
70
|
+
return error;
|
|
80
71
|
}
|
|
81
72
|
|
|
82
73
|
const createFilesystemSource = (options) => {
|
|
@@ -88,13 +79,10 @@ const createFilesystemSource = (options) => {
|
|
|
88
79
|
}
|
|
89
80
|
let stats;
|
|
90
81
|
try {
|
|
91
|
-
stats = await
|
|
92
|
-
} catch (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
} else {
|
|
96
|
-
throw createError("File access error " + err.code, 403, fsPath);
|
|
97
|
-
}
|
|
82
|
+
stats = await node_fs.promises.stat(fsPath);
|
|
83
|
+
} catch (error_) {
|
|
84
|
+
const error = error_.code === "ENOENT" ? createError("File not found", 404, fsPath) : createError("File access error " + error_.code, 403, fsPath);
|
|
85
|
+
throw error;
|
|
98
86
|
}
|
|
99
87
|
if (!stats.isFile()) {
|
|
100
88
|
throw createError("Path should be a file", 400, fsPath);
|
|
@@ -102,7 +90,7 @@ const createFilesystemSource = (options) => {
|
|
|
102
90
|
return {
|
|
103
91
|
mtime: stats.mtime,
|
|
104
92
|
maxAge: options.maxAge,
|
|
105
|
-
getData: cachedPromise(() =>
|
|
93
|
+
getData: cachedPromise(() => node_fs.promises.readFile(fsPath))
|
|
106
94
|
};
|
|
107
95
|
};
|
|
108
96
|
};
|
|
@@ -111,7 +99,7 @@ function isValidPath(fp) {
|
|
|
111
99
|
if (isWindows) {
|
|
112
100
|
fp = fp.slice(pathe.parse(fp).root.length);
|
|
113
101
|
}
|
|
114
|
-
if (/[
|
|
102
|
+
if (/["*:<>?|]/.test(fp)) {
|
|
115
103
|
return false;
|
|
116
104
|
}
|
|
117
105
|
return true;
|
|
@@ -119,24 +107,24 @@ function isValidPath(fp) {
|
|
|
119
107
|
|
|
120
108
|
const HTTP_RE = /^https?:\/\//;
|
|
121
109
|
const createHTTPSource = (options) => {
|
|
122
|
-
const httpsAgent = new
|
|
123
|
-
const httpAgent = new
|
|
110
|
+
const httpsAgent = new https.Agent({ keepAlive: true });
|
|
111
|
+
const httpAgent = new http.Agent({ keepAlive: true });
|
|
124
112
|
let _domains = options.domains || [];
|
|
125
113
|
if (typeof _domains === "string") {
|
|
126
114
|
_domains = _domains.split(",").map((s) => s.trim());
|
|
127
115
|
}
|
|
128
|
-
const domains = _domains.map((d) => {
|
|
116
|
+
const domains = new Set(_domains.map((d) => {
|
|
129
117
|
if (!HTTP_RE.test(d)) {
|
|
130
118
|
d = "http://" + d;
|
|
131
119
|
}
|
|
132
120
|
return new URL(d).hostname;
|
|
133
|
-
}).filter(Boolean);
|
|
134
|
-
return async (id,
|
|
121
|
+
}).filter(Boolean));
|
|
122
|
+
return async (id, requestOptions) => {
|
|
135
123
|
const hostname = new URL(id).hostname;
|
|
136
124
|
if (!hostname) {
|
|
137
125
|
throw createError("Hostname is missing", 403, id);
|
|
138
126
|
}
|
|
139
|
-
if (!
|
|
127
|
+
if (!requestOptions?.bypassDomain && !domains.has(hostname)) {
|
|
140
128
|
throw createError("Forbidden host", 403, hostname);
|
|
141
129
|
}
|
|
142
130
|
const response = await ohmyfetch.fetch(id, {
|
|
@@ -151,7 +139,7 @@ const createHTTPSource = (options) => {
|
|
|
151
139
|
if (_cacheControl) {
|
|
152
140
|
const m = _cacheControl.match(/max-age=(\d+)/);
|
|
153
141
|
if (m && m[1]) {
|
|
154
|
-
maxAge = parseInt(m[1]);
|
|
142
|
+
maxAge = Number.parseInt(m[1]);
|
|
155
143
|
}
|
|
156
144
|
}
|
|
157
145
|
let mtime;
|
|
@@ -167,19 +155,19 @@ const createHTTPSource = (options) => {
|
|
|
167
155
|
};
|
|
168
156
|
};
|
|
169
157
|
|
|
170
|
-
function VArg(
|
|
171
|
-
return
|
|
158
|
+
function VArg(argument) {
|
|
159
|
+
return destr(argument);
|
|
172
160
|
}
|
|
173
|
-
function parseArgs(
|
|
174
|
-
const vargs =
|
|
175
|
-
return mappers.map((v,
|
|
161
|
+
function parseArgs(arguments_, mappers) {
|
|
162
|
+
const vargs = arguments_.split("_");
|
|
163
|
+
return mappers.map((v, index) => v(vargs[index]));
|
|
176
164
|
}
|
|
177
165
|
function getHandler(key) {
|
|
178
166
|
return Handlers[key];
|
|
179
167
|
}
|
|
180
|
-
function applyHandler(
|
|
181
|
-
const
|
|
182
|
-
return handler.apply(
|
|
168
|
+
function applyHandler(context, pipe, handler, argumentsString) {
|
|
169
|
+
const arguments_ = handler.args ? parseArgs(argumentsString, handler.args) : [];
|
|
170
|
+
return handler.apply(context, pipe, ...arguments_);
|
|
183
171
|
}
|
|
184
172
|
function clampDimensionsPreservingAspectRatio(sourceDimensions, desiredDimensions) {
|
|
185
173
|
const desiredAspectRatio = desiredDimensions.width / desiredDimensions.height;
|
|
@@ -216,8 +204,8 @@ const position = {
|
|
|
216
204
|
context.position = position2;
|
|
217
205
|
}
|
|
218
206
|
};
|
|
219
|
-
const HEX_RE = /^([
|
|
220
|
-
const SHORTHEX_RE = /^([
|
|
207
|
+
const HEX_RE = /^([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i;
|
|
208
|
+
const SHORTHEX_RE = /^([\da-f])([\da-f])([\da-f])$/i;
|
|
221
209
|
const background = {
|
|
222
210
|
args: [VArg],
|
|
223
211
|
order: -1,
|
|
@@ -238,19 +226,19 @@ const enlarge = {
|
|
|
238
226
|
const width = {
|
|
239
227
|
args: [VArg],
|
|
240
228
|
apply: (context, pipe, width2) => {
|
|
241
|
-
return pipe.resize(width2,
|
|
229
|
+
return pipe.resize(width2, void 0, { withoutEnlargement: !context.enlarge });
|
|
242
230
|
}
|
|
243
231
|
};
|
|
244
232
|
const height = {
|
|
245
233
|
args: [VArg],
|
|
246
234
|
apply: (context, pipe, height2) => {
|
|
247
|
-
return pipe.resize(
|
|
235
|
+
return pipe.resize(void 0, height2, { withoutEnlargement: !context.enlarge });
|
|
248
236
|
}
|
|
249
237
|
};
|
|
250
238
|
const resize = {
|
|
251
239
|
args: [VArg, VArg, VArg],
|
|
252
240
|
apply: (context, pipe, size) => {
|
|
253
|
-
let [width2, height2] = String(size).split("x").map(
|
|
241
|
+
let [width2, height2] = String(size).split("x").map(Number);
|
|
254
242
|
if (!width2) {
|
|
255
243
|
return;
|
|
256
244
|
}
|
|
@@ -399,7 +387,7 @@ const h = height;
|
|
|
399
387
|
const s = resize;
|
|
400
388
|
const pos = position;
|
|
401
389
|
|
|
402
|
-
const SUPPORTED_FORMATS = ["jpeg", "png", "webp", "avif", "tiff", "gif"];
|
|
390
|
+
const SUPPORTED_FORMATS = /* @__PURE__ */ new Set(["jpeg", "png", "webp", "avif", "tiff", "gif"]);
|
|
403
391
|
function createIPX(userOptions) {
|
|
404
392
|
const defaults = {
|
|
405
393
|
dir: getEnv("IPX_DIR", "."),
|
|
@@ -409,44 +397,44 @@ function createIPX(userOptions) {
|
|
|
409
397
|
maxAge: getEnv("IPX_MAX_AGE", 300),
|
|
410
398
|
sharp: {}
|
|
411
399
|
};
|
|
412
|
-
const options =
|
|
400
|
+
const options = defu.defu(userOptions, defaults);
|
|
413
401
|
options.alias = Object.fromEntries(Object.entries(options.alias).map((e) => [ufo.withLeadingSlash(e[0]), e[1]]));
|
|
414
|
-
const
|
|
402
|
+
const context = {
|
|
415
403
|
sources: {}
|
|
416
404
|
};
|
|
417
405
|
if (options.dir) {
|
|
418
|
-
|
|
406
|
+
context.sources.filesystem = createFilesystemSource({
|
|
419
407
|
dir: options.dir,
|
|
420
408
|
maxAge: options.maxAge
|
|
421
409
|
});
|
|
422
410
|
}
|
|
423
411
|
if (options.domains) {
|
|
424
|
-
|
|
412
|
+
context.sources.http = createHTTPSource({
|
|
425
413
|
domains: options.domains,
|
|
426
414
|
fetchOptions: options.fetchOptions,
|
|
427
415
|
maxAge: options.maxAge
|
|
428
416
|
});
|
|
429
417
|
}
|
|
430
|
-
return function ipx(id, modifiers = {},
|
|
418
|
+
return function ipx(id, modifiers = {}, requestOptions = {}) {
|
|
431
419
|
if (!id) {
|
|
432
420
|
throw createError("resource id is missing", 400);
|
|
433
421
|
}
|
|
434
422
|
id = ufo.hasProtocol(id) ? id : ufo.withLeadingSlash(id);
|
|
435
423
|
for (const base in options.alias) {
|
|
436
424
|
if (id.startsWith(base)) {
|
|
437
|
-
id = ufo.joinURL(options.alias[base], id.
|
|
425
|
+
id = ufo.joinURL(options.alias[base], id.slice(base.length));
|
|
438
426
|
}
|
|
439
427
|
}
|
|
440
|
-
const
|
|
428
|
+
const getSource = cachedPromise(() => {
|
|
441
429
|
const source = ufo.hasProtocol(id) ? "http" : "filesystem";
|
|
442
|
-
if (!
|
|
430
|
+
if (!context.sources[source]) {
|
|
443
431
|
throw createError("Unknown source", 400, source);
|
|
444
432
|
}
|
|
445
|
-
return
|
|
433
|
+
return context.sources[source](id, requestOptions);
|
|
446
434
|
});
|
|
447
435
|
const getData = cachedPromise(async () => {
|
|
448
|
-
const
|
|
449
|
-
const data = await
|
|
436
|
+
const source = await getSource();
|
|
437
|
+
const data = await source.getData();
|
|
450
438
|
const meta = imageMeta.imageMeta(data);
|
|
451
439
|
const mFormat = modifiers.f || modifiers.format;
|
|
452
440
|
let format = mFormat || meta.type;
|
|
@@ -464,18 +452,18 @@ function createIPX(userOptions) {
|
|
|
464
452
|
const Sharp = await import('sharp').then((r) => r.default || r);
|
|
465
453
|
let sharp = Sharp(data, { animated });
|
|
466
454
|
Object.assign(sharp.options, options.sharp);
|
|
467
|
-
const handlers = Object.entries(modifiers).map(([name,
|
|
455
|
+
const handlers = Object.entries(modifiers).map(([name, arguments_]) => ({ handler: getHandler(name), name, args: arguments_ })).filter((h) => h.handler).sort((a, b) => {
|
|
468
456
|
const aKey = (a.handler.order || a.name || "").toString();
|
|
469
457
|
const bKey = (b.handler.order || b.name || "").toString();
|
|
470
458
|
return aKey.localeCompare(bKey);
|
|
471
459
|
});
|
|
472
|
-
const
|
|
460
|
+
const handlerContext = { meta };
|
|
473
461
|
for (const h of handlers) {
|
|
474
|
-
sharp = applyHandler(
|
|
462
|
+
sharp = applyHandler(handlerContext, sharp, h.handler, h.args) || sharp;
|
|
475
463
|
}
|
|
476
|
-
if (SUPPORTED_FORMATS.
|
|
464
|
+
if (SUPPORTED_FORMATS.has(format)) {
|
|
477
465
|
sharp = sharp.toFormat(format, {
|
|
478
|
-
quality:
|
|
466
|
+
quality: handlerContext.quality,
|
|
479
467
|
progressive: format === "jpeg"
|
|
480
468
|
});
|
|
481
469
|
}
|
|
@@ -487,54 +475,52 @@ function createIPX(userOptions) {
|
|
|
487
475
|
};
|
|
488
476
|
});
|
|
489
477
|
return {
|
|
490
|
-
src:
|
|
478
|
+
src: getSource,
|
|
491
479
|
data: getData
|
|
492
480
|
};
|
|
493
481
|
};
|
|
494
482
|
}
|
|
495
483
|
|
|
496
|
-
const MODIFIER_SEP = /[
|
|
497
|
-
const MODIFIER_VAL_SEP = /[_
|
|
498
|
-
async function _handleRequest(
|
|
484
|
+
const MODIFIER_SEP = /[&,]/g;
|
|
485
|
+
const MODIFIER_VAL_SEP = /[:=_]/g;
|
|
486
|
+
async function _handleRequest(request, ipx) {
|
|
499
487
|
const res = {
|
|
500
488
|
statusCode: 200,
|
|
501
489
|
statusMessage: "",
|
|
502
490
|
headers: {},
|
|
503
491
|
body: ""
|
|
504
492
|
};
|
|
505
|
-
const [
|
|
493
|
+
const [modifiersString = "", ...idSegments] = request.url.slice(1).split("/");
|
|
506
494
|
const id = safeString(ufo.decode(idSegments.join("/")));
|
|
507
|
-
if (!
|
|
508
|
-
throw createError("Modifiers are missing", 400,
|
|
495
|
+
if (!modifiersString) {
|
|
496
|
+
throw createError("Modifiers are missing", 400, request.url);
|
|
509
497
|
}
|
|
510
498
|
if (!id || id === "/") {
|
|
511
|
-
throw createError("Resource id is missing", 400,
|
|
499
|
+
throw createError("Resource id is missing", 400, request.url);
|
|
512
500
|
}
|
|
513
501
|
const modifiers = /* @__PURE__ */ Object.create(null);
|
|
514
|
-
if (
|
|
515
|
-
for (const p of
|
|
502
|
+
if (modifiersString !== "_") {
|
|
503
|
+
for (const p of modifiersString.split(MODIFIER_SEP)) {
|
|
516
504
|
const [key, value = ""] = p.split(MODIFIER_VAL_SEP);
|
|
517
505
|
modifiers[safeString(key)] = safeString(ufo.decode(value));
|
|
518
506
|
}
|
|
519
507
|
}
|
|
520
|
-
const img = ipx(id, modifiers,
|
|
521
|
-
const
|
|
522
|
-
if (
|
|
523
|
-
if (
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
return res;
|
|
527
|
-
}
|
|
508
|
+
const img = ipx(id, modifiers, request.options);
|
|
509
|
+
const source = await img.src();
|
|
510
|
+
if (source.mtime) {
|
|
511
|
+
if (request.headers["if-modified-since"] && new Date(request.headers["if-modified-since"]) >= source.mtime) {
|
|
512
|
+
res.statusCode = 304;
|
|
513
|
+
return res;
|
|
528
514
|
}
|
|
529
|
-
res.headers["Last-Modified"] =
|
|
515
|
+
res.headers["Last-Modified"] = source.mtime.toUTCString();
|
|
530
516
|
}
|
|
531
|
-
if (typeof
|
|
532
|
-
res.headers["Cache-Control"] = `max-age=${+
|
|
517
|
+
if (typeof source.maxAge === "number") {
|
|
518
|
+
res.headers["Cache-Control"] = `max-age=${+source.maxAge}, public, s-maxage=${+source.maxAge}`;
|
|
533
519
|
}
|
|
534
520
|
const { data, format } = await img.data();
|
|
535
|
-
const etag =
|
|
521
|
+
const etag = getEtag(data);
|
|
536
522
|
res.headers.ETag = etag;
|
|
537
|
-
if (etag &&
|
|
523
|
+
if (etag && request.headers["if-none-match"] === etag) {
|
|
538
524
|
res.statusCode = 304;
|
|
539
525
|
return res;
|
|
540
526
|
}
|
|
@@ -545,24 +531,24 @@ async function _handleRequest(req, ipx) {
|
|
|
545
531
|
res.body = data;
|
|
546
532
|
return sanetizeReponse(res);
|
|
547
533
|
}
|
|
548
|
-
function handleRequest(
|
|
549
|
-
return _handleRequest(
|
|
550
|
-
const statusCode = parseInt(
|
|
551
|
-
const statusMessage =
|
|
534
|
+
function handleRequest(request, ipx) {
|
|
535
|
+
return _handleRequest(request, ipx).catch((error) => {
|
|
536
|
+
const statusCode = Number.parseInt(error.statusCode) || 500;
|
|
537
|
+
const statusMessage = error.statusMessage ? error.statusMessage : `IPX Error (${statusCode})`;
|
|
552
538
|
if (process.env.NODE_ENV !== "production" && statusCode === 500) {
|
|
553
|
-
console.error(
|
|
539
|
+
console.error(error);
|
|
554
540
|
}
|
|
555
541
|
return sanetizeReponse({
|
|
556
542
|
statusCode,
|
|
557
543
|
statusMessage,
|
|
558
|
-
body: "IPX Error: " +
|
|
544
|
+
body: "IPX Error: " + error,
|
|
559
545
|
headers: {}
|
|
560
546
|
});
|
|
561
547
|
});
|
|
562
548
|
}
|
|
563
549
|
function createIPXMiddleware(ipx) {
|
|
564
|
-
return function IPXMiddleware(
|
|
565
|
-
return handleRequest({ url:
|
|
550
|
+
return function IPXMiddleware(request, res) {
|
|
551
|
+
return handleRequest({ url: request.url, headers: request.headers }, ipx).then((_res) => {
|
|
566
552
|
res.statusCode = _res.statusCode;
|
|
567
553
|
res.statusMessage = _res.statusMessage;
|
|
568
554
|
for (const name in _res.headers) {
|
|
@@ -577,7 +563,7 @@ function sanetizeReponse(res) {
|
|
|
577
563
|
statusCode: res.statusCode || 200,
|
|
578
564
|
statusMessage: res.statusMessage ? safeString(res.statusMessage) : "OK",
|
|
579
565
|
headers: safeStringObject(res.headers || {}),
|
|
580
|
-
body: typeof res.body === "string" ?
|
|
566
|
+
body: typeof res.body === "string" ? xss(safeString(res.body)) : res.body || ""
|
|
581
567
|
};
|
|
582
568
|
}
|
|
583
569
|
function safeString(input) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import defu from 'defu';
|
|
1
|
+
import { defu } from 'defu';
|
|
2
2
|
import { imageMeta } from 'image-meta';
|
|
3
3
|
import { withLeadingSlash, hasProtocol, joinURL, decode } from 'ufo';
|
|
4
|
-
import { promises } from 'fs';
|
|
4
|
+
import { promises } from 'node:fs';
|
|
5
5
|
import { resolve, join, parse } from 'pathe';
|
|
6
|
-
import http from 'http';
|
|
7
|
-
import https from 'https';
|
|
6
|
+
import http from 'node:http';
|
|
7
|
+
import https from 'node:https';
|
|
8
8
|
import { fetch } from 'ohmyfetch';
|
|
9
9
|
import destr from 'destr';
|
|
10
10
|
import getEtag from 'etag';
|
|
@@ -49,23 +49,23 @@ const Handlers = {
|
|
|
49
49
|
function getEnv(name, defaultValue) {
|
|
50
50
|
return destr(process.env[name]) ?? defaultValue;
|
|
51
51
|
}
|
|
52
|
-
function cachedPromise(
|
|
52
|
+
function cachedPromise(function_) {
|
|
53
53
|
let p;
|
|
54
|
-
return (...
|
|
54
|
+
return (...arguments_) => {
|
|
55
55
|
if (p) {
|
|
56
56
|
return p;
|
|
57
57
|
}
|
|
58
|
-
p = Promise.resolve(
|
|
58
|
+
p = Promise.resolve(function_(...arguments_));
|
|
59
59
|
return p;
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
class IPXError extends Error {
|
|
63
63
|
}
|
|
64
64
|
function createError(statusMessage, statusCode, trace) {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return
|
|
65
|
+
const error = new IPXError(statusMessage + (trace ? ` (${trace})` : ""));
|
|
66
|
+
error.statusMessage = "IPX: " + statusMessage;
|
|
67
|
+
error.statusCode = statusCode;
|
|
68
|
+
return error;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
const createFilesystemSource = (options) => {
|
|
@@ -78,12 +78,9 @@ const createFilesystemSource = (options) => {
|
|
|
78
78
|
let stats;
|
|
79
79
|
try {
|
|
80
80
|
stats = await promises.stat(fsPath);
|
|
81
|
-
} catch (
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
} else {
|
|
85
|
-
throw createError("File access error " + err.code, 403, fsPath);
|
|
86
|
-
}
|
|
81
|
+
} catch (error_) {
|
|
82
|
+
const error = error_.code === "ENOENT" ? createError("File not found", 404, fsPath) : createError("File access error " + error_.code, 403, fsPath);
|
|
83
|
+
throw error;
|
|
87
84
|
}
|
|
88
85
|
if (!stats.isFile()) {
|
|
89
86
|
throw createError("Path should be a file", 400, fsPath);
|
|
@@ -100,7 +97,7 @@ function isValidPath(fp) {
|
|
|
100
97
|
if (isWindows) {
|
|
101
98
|
fp = fp.slice(parse(fp).root.length);
|
|
102
99
|
}
|
|
103
|
-
if (/[
|
|
100
|
+
if (/["*:<>?|]/.test(fp)) {
|
|
104
101
|
return false;
|
|
105
102
|
}
|
|
106
103
|
return true;
|
|
@@ -114,18 +111,18 @@ const createHTTPSource = (options) => {
|
|
|
114
111
|
if (typeof _domains === "string") {
|
|
115
112
|
_domains = _domains.split(",").map((s) => s.trim());
|
|
116
113
|
}
|
|
117
|
-
const domains = _domains.map((d) => {
|
|
114
|
+
const domains = new Set(_domains.map((d) => {
|
|
118
115
|
if (!HTTP_RE.test(d)) {
|
|
119
116
|
d = "http://" + d;
|
|
120
117
|
}
|
|
121
118
|
return new URL(d).hostname;
|
|
122
|
-
}).filter(Boolean);
|
|
123
|
-
return async (id,
|
|
119
|
+
}).filter(Boolean));
|
|
120
|
+
return async (id, requestOptions) => {
|
|
124
121
|
const hostname = new URL(id).hostname;
|
|
125
122
|
if (!hostname) {
|
|
126
123
|
throw createError("Hostname is missing", 403, id);
|
|
127
124
|
}
|
|
128
|
-
if (!
|
|
125
|
+
if (!requestOptions?.bypassDomain && !domains.has(hostname)) {
|
|
129
126
|
throw createError("Forbidden host", 403, hostname);
|
|
130
127
|
}
|
|
131
128
|
const response = await fetch(id, {
|
|
@@ -140,7 +137,7 @@ const createHTTPSource = (options) => {
|
|
|
140
137
|
if (_cacheControl) {
|
|
141
138
|
const m = _cacheControl.match(/max-age=(\d+)/);
|
|
142
139
|
if (m && m[1]) {
|
|
143
|
-
maxAge = parseInt(m[1]);
|
|
140
|
+
maxAge = Number.parseInt(m[1]);
|
|
144
141
|
}
|
|
145
142
|
}
|
|
146
143
|
let mtime;
|
|
@@ -156,19 +153,19 @@ const createHTTPSource = (options) => {
|
|
|
156
153
|
};
|
|
157
154
|
};
|
|
158
155
|
|
|
159
|
-
function VArg(
|
|
160
|
-
return destr(
|
|
156
|
+
function VArg(argument) {
|
|
157
|
+
return destr(argument);
|
|
161
158
|
}
|
|
162
|
-
function parseArgs(
|
|
163
|
-
const vargs =
|
|
164
|
-
return mappers.map((v,
|
|
159
|
+
function parseArgs(arguments_, mappers) {
|
|
160
|
+
const vargs = arguments_.split("_");
|
|
161
|
+
return mappers.map((v, index) => v(vargs[index]));
|
|
165
162
|
}
|
|
166
163
|
function getHandler(key) {
|
|
167
164
|
return Handlers[key];
|
|
168
165
|
}
|
|
169
|
-
function applyHandler(
|
|
170
|
-
const
|
|
171
|
-
return handler.apply(
|
|
166
|
+
function applyHandler(context, pipe, handler, argumentsString) {
|
|
167
|
+
const arguments_ = handler.args ? parseArgs(argumentsString, handler.args) : [];
|
|
168
|
+
return handler.apply(context, pipe, ...arguments_);
|
|
172
169
|
}
|
|
173
170
|
function clampDimensionsPreservingAspectRatio(sourceDimensions, desiredDimensions) {
|
|
174
171
|
const desiredAspectRatio = desiredDimensions.width / desiredDimensions.height;
|
|
@@ -205,8 +202,8 @@ const position = {
|
|
|
205
202
|
context.position = position2;
|
|
206
203
|
}
|
|
207
204
|
};
|
|
208
|
-
const HEX_RE = /^([
|
|
209
|
-
const SHORTHEX_RE = /^([
|
|
205
|
+
const HEX_RE = /^([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i;
|
|
206
|
+
const SHORTHEX_RE = /^([\da-f])([\da-f])([\da-f])$/i;
|
|
210
207
|
const background = {
|
|
211
208
|
args: [VArg],
|
|
212
209
|
order: -1,
|
|
@@ -227,19 +224,19 @@ const enlarge = {
|
|
|
227
224
|
const width = {
|
|
228
225
|
args: [VArg],
|
|
229
226
|
apply: (context, pipe, width2) => {
|
|
230
|
-
return pipe.resize(width2,
|
|
227
|
+
return pipe.resize(width2, void 0, { withoutEnlargement: !context.enlarge });
|
|
231
228
|
}
|
|
232
229
|
};
|
|
233
230
|
const height = {
|
|
234
231
|
args: [VArg],
|
|
235
232
|
apply: (context, pipe, height2) => {
|
|
236
|
-
return pipe.resize(
|
|
233
|
+
return pipe.resize(void 0, height2, { withoutEnlargement: !context.enlarge });
|
|
237
234
|
}
|
|
238
235
|
};
|
|
239
236
|
const resize = {
|
|
240
237
|
args: [VArg, VArg, VArg],
|
|
241
238
|
apply: (context, pipe, size) => {
|
|
242
|
-
let [width2, height2] = String(size).split("x").map(
|
|
239
|
+
let [width2, height2] = String(size).split("x").map(Number);
|
|
243
240
|
if (!width2) {
|
|
244
241
|
return;
|
|
245
242
|
}
|
|
@@ -388,7 +385,7 @@ const h = height;
|
|
|
388
385
|
const s = resize;
|
|
389
386
|
const pos = position;
|
|
390
387
|
|
|
391
|
-
const SUPPORTED_FORMATS = ["jpeg", "png", "webp", "avif", "tiff", "gif"];
|
|
388
|
+
const SUPPORTED_FORMATS = /* @__PURE__ */ new Set(["jpeg", "png", "webp", "avif", "tiff", "gif"]);
|
|
392
389
|
function createIPX(userOptions) {
|
|
393
390
|
const defaults = {
|
|
394
391
|
dir: getEnv("IPX_DIR", "."),
|
|
@@ -400,42 +397,42 @@ function createIPX(userOptions) {
|
|
|
400
397
|
};
|
|
401
398
|
const options = defu(userOptions, defaults);
|
|
402
399
|
options.alias = Object.fromEntries(Object.entries(options.alias).map((e) => [withLeadingSlash(e[0]), e[1]]));
|
|
403
|
-
const
|
|
400
|
+
const context = {
|
|
404
401
|
sources: {}
|
|
405
402
|
};
|
|
406
403
|
if (options.dir) {
|
|
407
|
-
|
|
404
|
+
context.sources.filesystem = createFilesystemSource({
|
|
408
405
|
dir: options.dir,
|
|
409
406
|
maxAge: options.maxAge
|
|
410
407
|
});
|
|
411
408
|
}
|
|
412
409
|
if (options.domains) {
|
|
413
|
-
|
|
410
|
+
context.sources.http = createHTTPSource({
|
|
414
411
|
domains: options.domains,
|
|
415
412
|
fetchOptions: options.fetchOptions,
|
|
416
413
|
maxAge: options.maxAge
|
|
417
414
|
});
|
|
418
415
|
}
|
|
419
|
-
return function ipx(id, modifiers = {},
|
|
416
|
+
return function ipx(id, modifiers = {}, requestOptions = {}) {
|
|
420
417
|
if (!id) {
|
|
421
418
|
throw createError("resource id is missing", 400);
|
|
422
419
|
}
|
|
423
420
|
id = hasProtocol(id) ? id : withLeadingSlash(id);
|
|
424
421
|
for (const base in options.alias) {
|
|
425
422
|
if (id.startsWith(base)) {
|
|
426
|
-
id = joinURL(options.alias[base], id.
|
|
423
|
+
id = joinURL(options.alias[base], id.slice(base.length));
|
|
427
424
|
}
|
|
428
425
|
}
|
|
429
|
-
const
|
|
426
|
+
const getSource = cachedPromise(() => {
|
|
430
427
|
const source = hasProtocol(id) ? "http" : "filesystem";
|
|
431
|
-
if (!
|
|
428
|
+
if (!context.sources[source]) {
|
|
432
429
|
throw createError("Unknown source", 400, source);
|
|
433
430
|
}
|
|
434
|
-
return
|
|
431
|
+
return context.sources[source](id, requestOptions);
|
|
435
432
|
});
|
|
436
433
|
const getData = cachedPromise(async () => {
|
|
437
|
-
const
|
|
438
|
-
const data = await
|
|
434
|
+
const source = await getSource();
|
|
435
|
+
const data = await source.getData();
|
|
439
436
|
const meta = imageMeta(data);
|
|
440
437
|
const mFormat = modifiers.f || modifiers.format;
|
|
441
438
|
let format = mFormat || meta.type;
|
|
@@ -453,18 +450,18 @@ function createIPX(userOptions) {
|
|
|
453
450
|
const Sharp = await import('sharp').then((r) => r.default || r);
|
|
454
451
|
let sharp = Sharp(data, { animated });
|
|
455
452
|
Object.assign(sharp.options, options.sharp);
|
|
456
|
-
const handlers = Object.entries(modifiers).map(([name,
|
|
453
|
+
const handlers = Object.entries(modifiers).map(([name, arguments_]) => ({ handler: getHandler(name), name, args: arguments_ })).filter((h) => h.handler).sort((a, b) => {
|
|
457
454
|
const aKey = (a.handler.order || a.name || "").toString();
|
|
458
455
|
const bKey = (b.handler.order || b.name || "").toString();
|
|
459
456
|
return aKey.localeCompare(bKey);
|
|
460
457
|
});
|
|
461
|
-
const
|
|
458
|
+
const handlerContext = { meta };
|
|
462
459
|
for (const h of handlers) {
|
|
463
|
-
sharp = applyHandler(
|
|
460
|
+
sharp = applyHandler(handlerContext, sharp, h.handler, h.args) || sharp;
|
|
464
461
|
}
|
|
465
|
-
if (SUPPORTED_FORMATS.
|
|
462
|
+
if (SUPPORTED_FORMATS.has(format)) {
|
|
466
463
|
sharp = sharp.toFormat(format, {
|
|
467
|
-
quality:
|
|
464
|
+
quality: handlerContext.quality,
|
|
468
465
|
progressive: format === "jpeg"
|
|
469
466
|
});
|
|
470
467
|
}
|
|
@@ -476,54 +473,52 @@ function createIPX(userOptions) {
|
|
|
476
473
|
};
|
|
477
474
|
});
|
|
478
475
|
return {
|
|
479
|
-
src:
|
|
476
|
+
src: getSource,
|
|
480
477
|
data: getData
|
|
481
478
|
};
|
|
482
479
|
};
|
|
483
480
|
}
|
|
484
481
|
|
|
485
|
-
const MODIFIER_SEP = /[
|
|
486
|
-
const MODIFIER_VAL_SEP = /[_
|
|
487
|
-
async function _handleRequest(
|
|
482
|
+
const MODIFIER_SEP = /[&,]/g;
|
|
483
|
+
const MODIFIER_VAL_SEP = /[:=_]/g;
|
|
484
|
+
async function _handleRequest(request, ipx) {
|
|
488
485
|
const res = {
|
|
489
486
|
statusCode: 200,
|
|
490
487
|
statusMessage: "",
|
|
491
488
|
headers: {},
|
|
492
489
|
body: ""
|
|
493
490
|
};
|
|
494
|
-
const [
|
|
491
|
+
const [modifiersString = "", ...idSegments] = request.url.slice(1).split("/");
|
|
495
492
|
const id = safeString(decode(idSegments.join("/")));
|
|
496
|
-
if (!
|
|
497
|
-
throw createError("Modifiers are missing", 400,
|
|
493
|
+
if (!modifiersString) {
|
|
494
|
+
throw createError("Modifiers are missing", 400, request.url);
|
|
498
495
|
}
|
|
499
496
|
if (!id || id === "/") {
|
|
500
|
-
throw createError("Resource id is missing", 400,
|
|
497
|
+
throw createError("Resource id is missing", 400, request.url);
|
|
501
498
|
}
|
|
502
499
|
const modifiers = /* @__PURE__ */ Object.create(null);
|
|
503
|
-
if (
|
|
504
|
-
for (const p of
|
|
500
|
+
if (modifiersString !== "_") {
|
|
501
|
+
for (const p of modifiersString.split(MODIFIER_SEP)) {
|
|
505
502
|
const [key, value = ""] = p.split(MODIFIER_VAL_SEP);
|
|
506
503
|
modifiers[safeString(key)] = safeString(decode(value));
|
|
507
504
|
}
|
|
508
505
|
}
|
|
509
|
-
const img = ipx(id, modifiers,
|
|
510
|
-
const
|
|
511
|
-
if (
|
|
512
|
-
if (
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
return res;
|
|
516
|
-
}
|
|
506
|
+
const img = ipx(id, modifiers, request.options);
|
|
507
|
+
const source = await img.src();
|
|
508
|
+
if (source.mtime) {
|
|
509
|
+
if (request.headers["if-modified-since"] && new Date(request.headers["if-modified-since"]) >= source.mtime) {
|
|
510
|
+
res.statusCode = 304;
|
|
511
|
+
return res;
|
|
517
512
|
}
|
|
518
|
-
res.headers["Last-Modified"] =
|
|
513
|
+
res.headers["Last-Modified"] = source.mtime.toUTCString();
|
|
519
514
|
}
|
|
520
|
-
if (typeof
|
|
521
|
-
res.headers["Cache-Control"] = `max-age=${+
|
|
515
|
+
if (typeof source.maxAge === "number") {
|
|
516
|
+
res.headers["Cache-Control"] = `max-age=${+source.maxAge}, public, s-maxage=${+source.maxAge}`;
|
|
522
517
|
}
|
|
523
518
|
const { data, format } = await img.data();
|
|
524
519
|
const etag = getEtag(data);
|
|
525
520
|
res.headers.ETag = etag;
|
|
526
|
-
if (etag &&
|
|
521
|
+
if (etag && request.headers["if-none-match"] === etag) {
|
|
527
522
|
res.statusCode = 304;
|
|
528
523
|
return res;
|
|
529
524
|
}
|
|
@@ -534,24 +529,24 @@ async function _handleRequest(req, ipx) {
|
|
|
534
529
|
res.body = data;
|
|
535
530
|
return sanetizeReponse(res);
|
|
536
531
|
}
|
|
537
|
-
function handleRequest(
|
|
538
|
-
return _handleRequest(
|
|
539
|
-
const statusCode = parseInt(
|
|
540
|
-
const statusMessage =
|
|
532
|
+
function handleRequest(request, ipx) {
|
|
533
|
+
return _handleRequest(request, ipx).catch((error) => {
|
|
534
|
+
const statusCode = Number.parseInt(error.statusCode) || 500;
|
|
535
|
+
const statusMessage = error.statusMessage ? error.statusMessage : `IPX Error (${statusCode})`;
|
|
541
536
|
if (process.env.NODE_ENV !== "production" && statusCode === 500) {
|
|
542
|
-
console.error(
|
|
537
|
+
console.error(error);
|
|
543
538
|
}
|
|
544
539
|
return sanetizeReponse({
|
|
545
540
|
statusCode,
|
|
546
541
|
statusMessage,
|
|
547
|
-
body: "IPX Error: " +
|
|
542
|
+
body: "IPX Error: " + error,
|
|
548
543
|
headers: {}
|
|
549
544
|
});
|
|
550
545
|
});
|
|
551
546
|
}
|
|
552
547
|
function createIPXMiddleware(ipx) {
|
|
553
|
-
return function IPXMiddleware(
|
|
554
|
-
return handleRequest({ url:
|
|
548
|
+
return function IPXMiddleware(request, res) {
|
|
549
|
+
return handleRequest({ url: request.url, headers: request.headers }, ipx).then((_res) => {
|
|
555
550
|
res.statusCode = _res.statusCode;
|
|
556
551
|
res.statusMessage = _res.statusMessage;
|
|
557
552
|
for (const name in _res.headers) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ipx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-1",
|
|
4
4
|
"repository": "unjs/ipx",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"exports": {
|
|
@@ -17,43 +17,45 @@
|
|
|
17
17
|
"dist",
|
|
18
18
|
"bin"
|
|
19
19
|
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "unbuild",
|
|
22
|
+
"dev": "nodemon",
|
|
23
|
+
"lint": "eslint --ext .ts .",
|
|
24
|
+
"prepack": "pnpm build",
|
|
25
|
+
"release": "pnpm test && standard-version && git push --follow-tags && pnpm publish",
|
|
26
|
+
"start": "node bin/ipx.js",
|
|
27
|
+
"test": "pnpm lint && vitest run --coverage"
|
|
28
|
+
},
|
|
20
29
|
"dependencies": {
|
|
21
30
|
"consola": "^2.15.3",
|
|
22
|
-
"defu": "^6.1.
|
|
23
|
-
"destr": "^1.
|
|
31
|
+
"defu": "^6.1.1",
|
|
32
|
+
"destr": "^1.2.1",
|
|
24
33
|
"etag": "^1.8.1",
|
|
25
34
|
"image-meta": "^0.1.1",
|
|
26
|
-
"listhen": "^0.
|
|
27
|
-
"ohmyfetch": "^0.4.
|
|
28
|
-
"pathe": "^0.
|
|
29
|
-
"sharp": "^0.
|
|
30
|
-
"ufo": "^0.
|
|
35
|
+
"listhen": "^1.0.0",
|
|
36
|
+
"ohmyfetch": "^0.4.21",
|
|
37
|
+
"pathe": "^1.0.0",
|
|
38
|
+
"sharp": "^0.31.2",
|
|
39
|
+
"ufo": "^1.0.0",
|
|
31
40
|
"xss": "^1.0.14"
|
|
32
41
|
},
|
|
33
42
|
"devDependencies": {
|
|
34
|
-
"@nuxtjs/eslint-config-typescript": "^
|
|
43
|
+
"@nuxtjs/eslint-config-typescript": "^12.0.0",
|
|
35
44
|
"@types/etag": "^1.8.1",
|
|
36
45
|
"@types/is-valid-path": "^0.1.0",
|
|
37
46
|
"@types/node-fetch": "^2.6.2",
|
|
38
|
-
"@types/sharp": "^0.
|
|
39
|
-
"@vitest/coverage-c8": "^0.
|
|
40
|
-
"changelogen": "^0.
|
|
41
|
-
"eslint": "^8.
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
47
|
+
"@types/sharp": "^0.31.0",
|
|
48
|
+
"@vitest/coverage-c8": "^0.25.3",
|
|
49
|
+
"changelogen": "^0.4.0",
|
|
50
|
+
"eslint": "^8.28.0",
|
|
51
|
+
"eslint-config-unjs": "^0.0.2",
|
|
52
|
+
"jiti": "^1.16.0",
|
|
53
|
+
"nodemon": "^2.0.20",
|
|
54
|
+
"serve-handler": "^6.1.5",
|
|
45
55
|
"standard-version": "^9.5.0",
|
|
46
|
-
"typescript": "^4.
|
|
47
|
-
"unbuild": "^0.
|
|
48
|
-
"vitest": "^0.
|
|
56
|
+
"typescript": "^4.9.3",
|
|
57
|
+
"unbuild": "^1.0.1",
|
|
58
|
+
"vitest": "^0.25.3"
|
|
49
59
|
},
|
|
50
|
-
"packageManager": "pnpm@7.
|
|
51
|
-
|
|
52
|
-
"build": "unbuild",
|
|
53
|
-
"dev": "nodemon",
|
|
54
|
-
"lint": "eslint --ext .ts .",
|
|
55
|
-
"release": "pnpm test && standard-version && git push --follow-tags && pnpm publish",
|
|
56
|
-
"start": "node bin/ipx.js",
|
|
57
|
-
"test": "pnpm lint && vitest run --coverage"
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
+
"packageManager": "pnpm@7.17.0"
|
|
61
|
+
}
|